diff options
Diffstat (limited to 'arch/arm/mach-omap2/vc.c')
-rw-r--r-- | arch/arm/mach-omap2/vc.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c new file mode 100644 index 000000000000..031d116fbf10 --- /dev/null +++ b/arch/arm/mach-omap2/vc.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /* | ||
2 | * OMAP Voltage Controller (VC) interface | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments, Inc. | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/init.h> | ||
13 | |||
14 | #include <plat/cpu.h> | ||
15 | |||
16 | #include "voltage.h" | ||
17 | #include "vc.h" | ||
18 | #include "prm-regbits-34xx.h" | ||
19 | #include "prm-regbits-44xx.h" | ||
20 | #include "prm44xx.h" | ||
21 | |||
22 | /** | ||
23 | * struct omap_vc_channel_cfg - describe the cfg_channel bitfield | ||
24 | * @sa: bit for slave address | ||
25 | * @rav: bit for voltage configuration register | ||
26 | * @rac: bit for command configuration register | ||
27 | * @racen: enable bit for RAC | ||
28 | * @cmd: bit for command value set selection | ||
29 | * | ||
30 | * Channel configuration bits, common for OMAP3+ | ||
31 | * OMAP3 register: PRM_VC_CH_CONF | ||
32 | * OMAP4 register: PRM_VC_CFG_CHANNEL | ||
33 | * OMAP5 register: PRM_VC_SMPS_<voltdm>_CONFIG | ||
34 | */ | ||
35 | struct omap_vc_channel_cfg { | ||
36 | u8 sa; | ||
37 | u8 rav; | ||
38 | u8 rac; | ||
39 | u8 racen; | ||
40 | u8 cmd; | ||
41 | }; | ||
42 | |||
43 | static struct omap_vc_channel_cfg vc_default_channel_cfg = { | ||
44 | .sa = BIT(0), | ||
45 | .rav = BIT(1), | ||
46 | .rac = BIT(2), | ||
47 | .racen = BIT(3), | ||
48 | .cmd = BIT(4), | ||
49 | }; | ||
50 | |||
51 | /* | ||
52 | * On OMAP3+, all VC channels have the above default bitfield | ||
53 | * configuration, except the OMAP4 MPU channel. This appears | ||
54 | * to be a freak accident as every other VC channel has the | ||
55 | * default configuration, thus creating a mutant channel config. | ||
56 | */ | ||
57 | static struct omap_vc_channel_cfg vc_mutant_channel_cfg = { | ||
58 | .sa = BIT(0), | ||
59 | .rav = BIT(2), | ||
60 | .rac = BIT(3), | ||
61 | .racen = BIT(4), | ||
62 | .cmd = BIT(1), | ||
63 | }; | ||
64 | |||
65 | static struct omap_vc_channel_cfg *vc_cfg_bits; | ||
66 | #define CFG_CHANNEL_MASK 0x1f | ||
67 | |||
68 | /** | ||
69 | * omap_vc_config_channel - configure VC channel to PMIC mappings | ||
70 | * @voltdm: pointer to voltagdomain defining the desired VC channel | ||
71 | * | ||
72 | * Configures the VC channel to PMIC mappings for the following | ||
73 | * PMIC settings | ||
74 | * - i2c slave address (SA) | ||
75 | * - voltage configuration address (RAV) | ||
76 | * - command configuration address (RAC) and enable bit (RACEN) | ||
77 | * - command values for ON, ONLP, RET and OFF (CMD) | ||
78 | * | ||
79 | * This function currently only allows flexible configuration of the | ||
80 | * non-default channel. Starting with OMAP4, there are more than 2 | ||
81 | * channels, with one defined as the default (on OMAP4, it's MPU.) | ||
82 | * Only the non-default channel can be configured. | ||
83 | */ | ||
84 | static int omap_vc_config_channel(struct voltagedomain *voltdm) | ||
85 | { | ||
86 | struct omap_vc_channel *vc = voltdm->vc; | ||
87 | |||
88 | /* | ||
89 | * For default channel, the only configurable bit is RACEN. | ||
90 | * All others must stay at zero (see function comment above.) | ||
91 | */ | ||
92 | if (vc->flags & OMAP_VC_CHANNEL_DEFAULT) | ||
93 | vc->cfg_channel &= vc_cfg_bits->racen; | ||
94 | |||
95 | voltdm->rmw(CFG_CHANNEL_MASK << vc->cfg_channel_sa_shift, | ||
96 | vc->cfg_channel << vc->cfg_channel_sa_shift, | ||
97 | vc->cfg_channel_reg); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /* Voltage scale and accessory APIs */ | ||
103 | int omap_vc_pre_scale(struct voltagedomain *voltdm, | ||
104 | unsigned long target_volt, | ||
105 | u8 *target_vsel, u8 *current_vsel) | ||
106 | { | ||
107 | struct omap_vc_channel *vc = voltdm->vc; | ||
108 | u32 vc_cmdval; | ||
109 | |||
110 | /* Check if sufficient pmic info is available for this vdd */ | ||
111 | if (!voltdm->pmic) { | ||
112 | pr_err("%s: Insufficient pmic info to scale the vdd_%s\n", | ||
113 | __func__, voltdm->name); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | if (!voltdm->pmic->uv_to_vsel) { | ||
118 | pr_err("%s: PMIC function to convert voltage in uV to" | ||
119 | "vsel not registered. Hence unable to scale voltage" | ||
120 | "for vdd_%s\n", __func__, voltdm->name); | ||
121 | return -ENODATA; | ||
122 | } | ||
123 | |||
124 | if (!voltdm->read || !voltdm->write) { | ||
125 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | ||
126 | __func__, voltdm->name); | ||
127 | return -EINVAL; | ||
128 | } | ||
129 | |||
130 | *target_vsel = voltdm->pmic->uv_to_vsel(target_volt); | ||
131 | *current_vsel = voltdm->pmic->uv_to_vsel(voltdm->nominal_volt); | ||
132 | |||
133 | /* Setting the ON voltage to the new target voltage */ | ||
134 | vc_cmdval = voltdm->read(vc->cmdval_reg); | ||
135 | vc_cmdval &= ~vc->common->cmd_on_mask; | ||
136 | vc_cmdval |= (*target_vsel << vc->common->cmd_on_shift); | ||
137 | voltdm->write(vc_cmdval, vc->cmdval_reg); | ||
138 | |||
139 | omap_vp_update_errorgain(voltdm, target_volt); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | void omap_vc_post_scale(struct voltagedomain *voltdm, | ||
145 | unsigned long target_volt, | ||
146 | u8 target_vsel, u8 current_vsel) | ||
147 | { | ||
148 | u32 smps_steps = 0, smps_delay = 0; | ||
149 | |||
150 | smps_steps = abs(target_vsel - current_vsel); | ||
151 | /* SMPS slew rate / step size. 2us added as buffer. */ | ||
152 | smps_delay = ((smps_steps * voltdm->pmic->step_size) / | ||
153 | voltdm->pmic->slew_rate) + 2; | ||
154 | udelay(smps_delay); | ||
155 | } | ||
156 | |||
157 | /* vc_bypass_scale - VC bypass method of voltage scaling */ | ||
158 | int omap_vc_bypass_scale(struct voltagedomain *voltdm, | ||
159 | unsigned long target_volt) | ||
160 | { | ||
161 | struct omap_vc_channel *vc = voltdm->vc; | ||
162 | u32 loop_cnt = 0, retries_cnt = 0; | ||
163 | u32 vc_valid, vc_bypass_val_reg, vc_bypass_value; | ||
164 | u8 target_vsel, current_vsel; | ||
165 | int ret; | ||
166 | |||
167 | ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); | ||
168 | if (ret) | ||
169 | return ret; | ||
170 | |||
171 | vc_valid = vc->common->valid; | ||
172 | vc_bypass_val_reg = vc->common->bypass_val_reg; | ||
173 | vc_bypass_value = (target_vsel << vc->common->data_shift) | | ||
174 | (vc->volt_reg_addr << vc->common->regaddr_shift) | | ||
175 | (vc->i2c_slave_addr << vc->common->slaveaddr_shift); | ||
176 | |||
177 | voltdm->write(vc_bypass_value, vc_bypass_val_reg); | ||
178 | voltdm->write(vc_bypass_value | vc_valid, vc_bypass_val_reg); | ||
179 | |||
180 | vc_bypass_value = voltdm->read(vc_bypass_val_reg); | ||
181 | /* | ||
182 | * Loop till the bypass command is acknowledged from the SMPS. | ||
183 | * NOTE: This is legacy code. The loop count and retry count needs | ||
184 | * to be revisited. | ||
185 | */ | ||
186 | while (!(vc_bypass_value & vc_valid)) { | ||
187 | loop_cnt++; | ||
188 | |||
189 | if (retries_cnt > 10) { | ||
190 | pr_warning("%s: Retry count exceeded\n", __func__); | ||
191 | return -ETIMEDOUT; | ||
192 | } | ||
193 | |||
194 | if (loop_cnt > 50) { | ||
195 | retries_cnt++; | ||
196 | loop_cnt = 0; | ||
197 | udelay(10); | ||
198 | } | ||
199 | vc_bypass_value = voltdm->read(vc_bypass_val_reg); | ||
200 | } | ||
201 | |||
202 | omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void __init omap3_vfsm_init(struct voltagedomain *voltdm) | ||
207 | { | ||
208 | /* | ||
209 | * Voltage Manager FSM parameters init | ||
210 | * XXX This data should be passed in from the board file | ||
211 | */ | ||
212 | voltdm->write(OMAP3_CLKSETUP, OMAP3_PRM_CLKSETUP_OFFSET); | ||
213 | voltdm->write(OMAP3_VOLTOFFSET, OMAP3_PRM_VOLTOFFSET_OFFSET); | ||
214 | voltdm->write(OMAP3_VOLTSETUP2, OMAP3_PRM_VOLTSETUP2_OFFSET); | ||
215 | } | ||
216 | |||
217 | static void __init omap3_vc_init_channel(struct voltagedomain *voltdm) | ||
218 | { | ||
219 | static bool is_initialized; | ||
220 | |||
221 | if (is_initialized) | ||
222 | return; | ||
223 | |||
224 | omap3_vfsm_init(voltdm); | ||
225 | |||
226 | is_initialized = true; | ||
227 | } | ||
228 | |||
229 | |||
230 | /* OMAP4 specific voltage init functions */ | ||
231 | static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) | ||
232 | { | ||
233 | static bool is_initialized; | ||
234 | u32 vc_val; | ||
235 | |||
236 | if (is_initialized) | ||
237 | return; | ||
238 | |||
239 | /* XXX These are magic numbers and do not belong! */ | ||
240 | vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT); | ||
241 | voltdm->write(vc_val, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET); | ||
242 | |||
243 | is_initialized = true; | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * omap_vc_i2c_init - initialize I2C interface to PMIC | ||
248 | * @voltdm: voltage domain containing VC data | ||
249 | * | ||
250 | * Use PMIC supplied seetings for I2C high-speed mode and | ||
251 | * master code (if set) and program the VC I2C configuration | ||
252 | * register. | ||
253 | * | ||
254 | * The VC I2C configuration is common to all VC channels, | ||
255 | * so this function only configures I2C for the first VC | ||
256 | * channel registers. All other VC channels will use the | ||
257 | * same configuration. | ||
258 | */ | ||
259 | static void __init omap_vc_i2c_init(struct voltagedomain *voltdm) | ||
260 | { | ||
261 | struct omap_vc_channel *vc = voltdm->vc; | ||
262 | static bool initialized; | ||
263 | static bool i2c_high_speed; | ||
264 | u8 mcode; | ||
265 | |||
266 | if (initialized) { | ||
267 | if (voltdm->pmic->i2c_high_speed != i2c_high_speed) | ||
268 | pr_warn("%s: I2C config for all channels must match.", | ||
269 | __func__); | ||
270 | return; | ||
271 | } | ||
272 | |||
273 | i2c_high_speed = voltdm->pmic->i2c_high_speed; | ||
274 | if (i2c_high_speed) | ||
275 | voltdm->rmw(vc->common->i2c_cfg_hsen_mask, | ||
276 | vc->common->i2c_cfg_hsen_mask, | ||
277 | vc->common->i2c_cfg_reg); | ||
278 | |||
279 | mcode = voltdm->pmic->i2c_mcode; | ||
280 | if (mcode) | ||
281 | voltdm->rmw(vc->common->i2c_mcode_mask, | ||
282 | mcode << __ffs(vc->common->i2c_mcode_mask), | ||
283 | vc->common->i2c_cfg_reg); | ||
284 | |||
285 | initialized = true; | ||
286 | } | ||
287 | |||
288 | void __init omap_vc_init_channel(struct voltagedomain *voltdm) | ||
289 | { | ||
290 | struct omap_vc_channel *vc = voltdm->vc; | ||
291 | u8 on_vsel, onlp_vsel, ret_vsel, off_vsel; | ||
292 | u32 val; | ||
293 | |||
294 | if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { | ||
295 | pr_err("%s: PMIC info requried to configure vc for" | ||
296 | "vdd_%s not populated.Hence cannot initialize vc\n", | ||
297 | __func__, voltdm->name); | ||
298 | return; | ||
299 | } | ||
300 | |||
301 | if (!voltdm->read || !voltdm->write) { | ||
302 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | ||
303 | __func__, voltdm->name); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | vc->cfg_channel = 0; | ||
308 | if (vc->flags & OMAP_VC_CHANNEL_CFG_MUTANT) | ||
309 | vc_cfg_bits = &vc_mutant_channel_cfg; | ||
310 | else | ||
311 | vc_cfg_bits = &vc_default_channel_cfg; | ||
312 | |||
313 | /* get PMIC/board specific settings */ | ||
314 | vc->i2c_slave_addr = voltdm->pmic->i2c_slave_addr; | ||
315 | vc->volt_reg_addr = voltdm->pmic->volt_reg_addr; | ||
316 | vc->cmd_reg_addr = voltdm->pmic->cmd_reg_addr; | ||
317 | vc->setup_time = voltdm->pmic->volt_setup_time; | ||
318 | |||
319 | /* Configure the i2c slave address for this VC */ | ||
320 | voltdm->rmw(vc->smps_sa_mask, | ||
321 | vc->i2c_slave_addr << __ffs(vc->smps_sa_mask), | ||
322 | vc->smps_sa_reg); | ||
323 | vc->cfg_channel |= vc_cfg_bits->sa; | ||
324 | |||
325 | /* | ||
326 | * Configure the PMIC register addresses. | ||
327 | */ | ||
328 | voltdm->rmw(vc->smps_volra_mask, | ||
329 | vc->volt_reg_addr << __ffs(vc->smps_volra_mask), | ||
330 | vc->smps_volra_reg); | ||
331 | vc->cfg_channel |= vc_cfg_bits->rav; | ||
332 | |||
333 | if (vc->cmd_reg_addr) { | ||
334 | voltdm->rmw(vc->smps_cmdra_mask, | ||
335 | vc->cmd_reg_addr << __ffs(vc->smps_cmdra_mask), | ||
336 | vc->smps_cmdra_reg); | ||
337 | vc->cfg_channel |= vc_cfg_bits->rac | vc_cfg_bits->racen; | ||
338 | } | ||
339 | |||
340 | /* Set up the on, inactive, retention and off voltage */ | ||
341 | on_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->on_volt); | ||
342 | onlp_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->onlp_volt); | ||
343 | ret_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->ret_volt); | ||
344 | off_vsel = voltdm->pmic->uv_to_vsel(voltdm->pmic->off_volt); | ||
345 | val = ((on_vsel << vc->common->cmd_on_shift) | | ||
346 | (onlp_vsel << vc->common->cmd_onlp_shift) | | ||
347 | (ret_vsel << vc->common->cmd_ret_shift) | | ||
348 | (off_vsel << vc->common->cmd_off_shift)); | ||
349 | voltdm->write(val, vc->cmdval_reg); | ||
350 | vc->cfg_channel |= vc_cfg_bits->cmd; | ||
351 | |||
352 | /* Channel configuration */ | ||
353 | omap_vc_config_channel(voltdm); | ||
354 | |||
355 | /* Configure the setup times */ | ||
356 | voltdm->rmw(voltdm->vfsm->voltsetup_mask, | ||
357 | vc->setup_time << __ffs(voltdm->vfsm->voltsetup_mask), | ||
358 | voltdm->vfsm->voltsetup_reg); | ||
359 | |||
360 | omap_vc_i2c_init(voltdm); | ||
361 | |||
362 | if (cpu_is_omap34xx()) | ||
363 | omap3_vc_init_channel(voltdm); | ||
364 | else if (cpu_is_omap44xx()) | ||
365 | omap4_vc_init_channel(voltdm); | ||
366 | } | ||
367 | |||