]> Pileus Git - ~andy/linux/blob - arch/arm/mach-omap2/vp.c
OMAP: VP: Explicitly mask VPVOLTAGE field
[~andy/linux] / arch / arm / mach-omap2 / vp.c
1 #include <linux/kernel.h>
2 #include <linux/init.h>
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
12 static void vp_latch_vsel(struct voltagedomain *voltdm)
13 {
14         struct omap_vp_instance *vp = voltdm->vp;
15         u32 vpconfig;
16         unsigned long uvdc;
17         char vsel;
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
26         if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
27                 pr_warning("%s: PMIC function to convert voltage in uV to"
28                         " vsel not registered\n", __func__);
29                 return;
30         }
31
32         vsel = voltdm->pmic->uv_to_vsel(uvdc);
33
34         vpconfig = voltdm->read(vp->vpconfig);
35         vpconfig &= ~(vp->common->vpconfig_initvoltage_mask |
36                         vp->common->vpconfig_initvdd);
37         vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask);
38         voltdm->write(vpconfig, vp->vpconfig);
39
40         /* Trigger initVDD value copy to voltage processor */
41         voltdm->write((vpconfig | vp->common->vpconfig_initvdd),
42                        vp->vpconfig);
43
44         /* Clear initVDD copy trigger bit */
45         voltdm->write(vpconfig, vp->vpconfig);
46 }
47
48 /* Generic voltage init functions */
49 void __init omap_vp_init(struct voltagedomain *voltdm)
50 {
51         struct omap_vp_instance *vp = voltdm->vp;
52         u32 val, sys_clk_rate, timeout, waittime;
53         u32 vddmin, vddmax, vstepmin, vstepmax;
54
55         if (!voltdm->read || !voltdm->write) {
56                 pr_err("%s: No read/write API for accessing vdd_%s regs\n",
57                         __func__, voltdm->name);
58                 return;
59         }
60
61         vp->enabled = false;
62
63         /* Divide to avoid overflow */
64         sys_clk_rate = voltdm->sys_clk.rate / 1000;
65
66         timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000;
67         vddmin = voltdm->pmic->vp_vddmin;
68         vddmax = voltdm->pmic->vp_vddmax;
69
70         waittime = ((voltdm->pmic->step_size / voltdm->pmic->slew_rate) *
71                     sys_clk_rate) / 1000;
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)) |
81                 vp->common->vpconfig_timeouten;
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);
99 }
100
101 int omap_vp_update_errorgain(struct voltagedomain *voltdm,
102                              unsigned long target_volt)
103 {
104         struct omap_volt_data *volt_data;
105
106         /* Get volt_data corresponding to target_volt */
107         volt_data = omap_voltage_get_voltdata(voltdm, target_volt);
108         if (IS_ERR(volt_data))
109                 return -EINVAL;
110
111         /* Setting vp errorgain based on the voltage */
112         voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask,
113                     volt_data->vp_errgain <<
114                     __ffs(voltdm->vp->common->vpconfig_errorgain_mask),
115                     voltdm->vp->vpconfig);
116
117         return 0;
118 }
119
120 /* VP force update method of voltage scaling */
121 int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
122                               unsigned long target_volt)
123 {
124         struct omap_vp_instance *vp = voltdm->vp;
125         u32 vpconfig;
126         u8 target_vsel, current_vsel;
127         int ret, timeout = 0;
128
129         ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
130         if (ret)
131                 return ret;
132
133         /*
134          * Clear all pending TransactionDone interrupt/status. Typical latency
135          * is <3us
136          */
137         while (timeout++ < VP_TRANXDONE_TIMEOUT) {
138                 vp->common->ops->clear_txdone(vp->id);
139                 if (!vp->common->ops->check_txdone(vp->id))
140                         break;
141                 udelay(1);
142         }
143         if (timeout >= VP_TRANXDONE_TIMEOUT) {
144                 pr_warning("%s: vdd_%s TRANXDONE timeout exceeded."
145                         "Voltage change aborted", __func__, voltdm->name);
146                 return -ETIMEDOUT;
147         }
148
149         /* Configure for VP-Force Update */
150         vpconfig = voltdm->read(vp->vpconfig);
151         vpconfig &= ~(vp->common->vpconfig_initvdd |
152                         vp->common->vpconfig_forceupdate |
153                         vp->common->vpconfig_initvoltage_mask);
154         vpconfig |= ((target_vsel <<
155                       __ffs(vp->common->vpconfig_initvoltage_mask)));
156         voltdm->write(vpconfig, vp->vpconfig);
157
158         /* Trigger initVDD value copy to voltage processor */
159         vpconfig |= vp->common->vpconfig_initvdd;
160         voltdm->write(vpconfig, vp->vpconfig);
161
162         /* Force update of voltage */
163         vpconfig |= vp->common->vpconfig_forceupdate;
164         voltdm->write(vpconfig, vp->vpconfig);
165
166         /*
167          * Wait for TransactionDone. Typical latency is <200us.
168          * Depends on SMPSWAITTIMEMIN/MAX and voltage change
169          */
170         timeout = 0;
171         omap_test_timeout(vp->common->ops->check_txdone(vp->id),
172                           VP_TRANXDONE_TIMEOUT, timeout);
173         if (timeout >= VP_TRANXDONE_TIMEOUT)
174                 pr_err("%s: vdd_%s TRANXDONE timeout exceeded."
175                         "TRANXDONE never got set after the voltage update\n",
176                         __func__, voltdm->name);
177
178         omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
179
180         /*
181          * Disable TransactionDone interrupt , clear all status, clear
182          * control registers
183          */
184         timeout = 0;
185         while (timeout++ < VP_TRANXDONE_TIMEOUT) {
186                 vp->common->ops->clear_txdone(vp->id);
187                 if (!vp->common->ops->check_txdone(vp->id))
188                         break;
189                 udelay(1);
190         }
191
192         if (timeout >= VP_TRANXDONE_TIMEOUT)
193                 pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying"
194                         "to clear the TRANXDONE status\n",
195                         __func__, voltdm->name);
196
197         vpconfig = voltdm->read(vp->vpconfig);
198         /* Clear initVDD copy trigger bit */
199         vpconfig &= ~vp->common->vpconfig_initvdd;
200         voltdm->write(vpconfig, vp->vpconfig);
201         /* Clear force bit */
202         vpconfig &= ~vp->common->vpconfig_forceupdate;
203         voltdm->write(vpconfig, vp->vpconfig);
204
205         return 0;
206 }
207
208 /**
209  * omap_vp_get_curr_volt() - API to get the current vp voltage.
210  * @voltdm:     pointer to the VDD.
211  *
212  * This API returns the current voltage for the specified voltage processor
213  */
214 unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm)
215 {
216         struct omap_vp_instance *vp = voltdm->vp;
217         u8 curr_vsel;
218
219         if (!voltdm || IS_ERR(voltdm)) {
220                 pr_warning("%s: VDD specified does not exist!\n", __func__);
221                 return 0;
222         }
223
224         if (!voltdm->read) {
225                 pr_err("%s: No read API for reading vdd_%s regs\n",
226                         __func__, voltdm->name);
227                 return 0;
228         }
229
230         curr_vsel = (voltdm->read(vp->voltage) & vp->common->vpvoltage_mask)
231                 >> __ffs(vp->common->vpvoltage_mask);
232
233         if (!voltdm->pmic || !voltdm->pmic->vsel_to_uv) {
234                 pr_warning("%s: PMIC function to convert vsel to voltage"
235                         "in uV not registerd\n", __func__);
236                 return 0;
237         }
238
239         return voltdm->pmic->vsel_to_uv(curr_vsel);
240 }
241
242 /**
243  * omap_vp_enable() - API to enable a particular VP
244  * @voltdm:     pointer to the VDD whose VP is to be enabled.
245  *
246  * This API enables a particular voltage processor. Needed by the smartreflex
247  * class drivers.
248  */
249 void omap_vp_enable(struct voltagedomain *voltdm)
250 {
251         struct omap_vp_instance *vp;
252         u32 vpconfig;
253
254         if (!voltdm || IS_ERR(voltdm)) {
255                 pr_warning("%s: VDD specified does not exist!\n", __func__);
256                 return;
257         }
258
259         vp = voltdm->vp;
260         if (!voltdm->read || !voltdm->write) {
261                 pr_err("%s: No read/write API for accessing vdd_%s regs\n",
262                         __func__, voltdm->name);
263                 return;
264         }
265
266         /* If VP is already enabled, do nothing. Return */
267         if (vp->enabled)
268                 return;
269
270         vp_latch_vsel(voltdm);
271
272         /* Enable VP */
273         vpconfig = voltdm->read(vp->vpconfig);
274         vpconfig |= vp->common->vpconfig_vpenable;
275         voltdm->write(vpconfig, vp->vpconfig);
276         vp->enabled = true;
277 }
278
279 /**
280  * omap_vp_disable() - API to disable a particular VP
281  * @voltdm:     pointer to the VDD whose VP is to be disabled.
282  *
283  * This API disables a particular voltage processor. Needed by the smartreflex
284  * class drivers.
285  */
286 void omap_vp_disable(struct voltagedomain *voltdm)
287 {
288         struct omap_vp_instance *vp;
289         u32 vpconfig;
290         int timeout;
291
292         if (!voltdm || IS_ERR(voltdm)) {
293                 pr_warning("%s: VDD specified does not exist!\n", __func__);
294                 return;
295         }
296
297         vp = voltdm->vp;
298         if (!voltdm->read || !voltdm->write) {
299                 pr_err("%s: No read/write API for accessing vdd_%s regs\n",
300                         __func__, voltdm->name);
301                 return;
302         }
303
304         /* If VP is already disabled, do nothing. Return */
305         if (!vp->enabled) {
306                 pr_warning("%s: Trying to disable VP for vdd_%s when"
307                         "it is already disabled\n", __func__, voltdm->name);
308                 return;
309         }
310
311         /* Disable VP */
312         vpconfig = voltdm->read(vp->vpconfig);
313         vpconfig &= ~vp->common->vpconfig_vpenable;
314         voltdm->write(vpconfig, vp->vpconfig);
315
316         /*
317          * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
318          */
319         omap_test_timeout((voltdm->read(vp->vstatus)),
320                           VP_IDLE_TIMEOUT, timeout);
321
322         if (timeout >= VP_IDLE_TIMEOUT)
323                 pr_warning("%s: vdd_%s idle timedout\n",
324                         __func__, voltdm->name);
325
326         vp->enabled = false;
327
328         return;
329 }