Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
voltage.c
Go to the documentation of this file.
1 /*
2  * OMAP3/OMAP4 Voltage Management Routines
3  *
4  * Author: Thara Gopinath <[email protected]>
5  *
6  * Copyright (C) 2007 Texas Instruments, Inc.
7  * Rajendra Nayak <[email protected]>
8  * Lesly A M <[email protected]>
9  *
10  * Copyright (C) 2008, 2011 Nokia Corporation
11  * Kalle Jokiniemi
12  * Paul Walmsley
13  *
14  * Copyright (C) 2010 Texas Instruments, Inc.
15  * Thara Gopinath <[email protected]>
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/err.h>
25 #include <linux/export.h>
26 #include <linux/debugfs.h>
27 #include <linux/slab.h>
28 #include <linux/clk.h>
29 
30 #include "common.h"
31 
32 #include "prm-regbits-34xx.h"
33 #include "prm-regbits-44xx.h"
34 #include "prm44xx.h"
35 #include "prcm44xx.h"
36 #include "prminst44xx.h"
37 #include "control.h"
38 
39 #include "voltage.h"
40 #include "powerdomain.h"
41 
42 #include "vc.h"
43 #include "vp.h"
44 
45 static LIST_HEAD(voltdm_list);
46 
47 /* Public functions */
55 unsigned long voltdm_get_voltage(struct voltagedomain *voltdm)
56 {
57  if (!voltdm || IS_ERR(voltdm)) {
58  pr_warning("%s: VDD specified does not exist!\n", __func__);
59  return 0;
60  }
61 
62  return voltdm->nominal_volt;
63 }
64 
73 int voltdm_scale(struct voltagedomain *voltdm,
74  unsigned long target_volt)
75 {
76  int ret, i;
77  unsigned long volt = 0;
78 
79  if (!voltdm || IS_ERR(voltdm)) {
80  pr_warning("%s: VDD specified does not exist!\n", __func__);
81  return -EINVAL;
82  }
83 
84  if (!voltdm->scale) {
85  pr_err("%s: No voltage scale API registered for vdd_%s\n",
86  __func__, voltdm->name);
87  return -ENODATA;
88  }
89 
90  /* Adjust voltage to the exact voltage from the OPP table */
91  for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
92  if (voltdm->volt_data[i].volt_nominal >= target_volt) {
93  volt = voltdm->volt_data[i].volt_nominal;
94  break;
95  }
96  }
97 
98  if (!volt) {
99  pr_warning("%s: not scaling. OPP voltage for %lu, not found.\n",
100  __func__, target_volt);
101  return -EINVAL;
102  }
103 
104  ret = voltdm->scale(voltdm, volt);
105  if (!ret)
106  voltdm->nominal_volt = volt;
107 
108  return ret;
109 }
110 
120 void voltdm_reset(struct voltagedomain *voltdm)
121 {
122  unsigned long target_volt;
123 
124  if (!voltdm || IS_ERR(voltdm)) {
125  pr_warning("%s: VDD specified does not exist!\n", __func__);
126  return;
127  }
128 
129  target_volt = voltdm_get_voltage(voltdm);
130  if (!target_volt) {
131  pr_err("%s: unable to find current voltage for vdd_%s\n",
132  __func__, voltdm->name);
133  return;
134  }
135 
136  voltdm_scale(voltdm, target_volt);
137 }
138 
152  struct omap_volt_data **volt_data)
153 {
154  if (!voltdm || IS_ERR(voltdm)) {
155  pr_warning("%s: VDD specified does not exist!\n", __func__);
156  return;
157  }
158 
159  *volt_data = voltdm->volt_data;
160 }
161 
178  unsigned long volt)
179 {
180  int i;
181 
182  if (!voltdm || IS_ERR(voltdm)) {
183  pr_warning("%s: VDD specified does not exist!\n", __func__);
184  return ERR_PTR(-EINVAL);
185  }
186 
187  if (!voltdm->volt_data) {
188  pr_warning("%s: voltage table does not exist for vdd_%s\n",
189  __func__, voltdm->name);
190  return ERR_PTR(-ENODATA);
191  }
192 
193  for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
194  if (voltdm->volt_data[i].volt_nominal == volt)
195  return &voltdm->volt_data[i];
196  }
197 
198  pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n",
199  __func__, voltdm->name);
200 
201  return ERR_PTR(-ENODATA);
202 }
203 
214  struct omap_voltdm_pmic *pmic)
215 {
216  if (!voltdm || IS_ERR(voltdm)) {
217  pr_warning("%s: VDD specified does not exist!\n", __func__);
218  return -EINVAL;
219  }
220 
221  voltdm->pmic = pmic;
222 
223  return 0;
224 }
225 
237  int voltscale_method)
238 {
239  if (!voltdm || IS_ERR(voltdm)) {
240  pr_warning("%s: VDD specified does not exist!\n", __func__);
241  return;
242  }
243 
244  switch (voltscale_method) {
247  return;
248  case VOLTSCALE_VCBYPASS:
249  voltdm->scale = omap_vc_bypass_scale;
250  return;
251  default:
252  pr_warn("%s: Trying to change the method of voltage scaling to an unsupported one!\n",
253  __func__);
254  }
255 }
256 
265 {
266  struct voltagedomain *voltdm;
267 
268  if (list_empty(&voltdm_list)) {
269  pr_err("%s: Voltage driver support not added\n",
270  __func__);
271  return -EINVAL;
272  }
273 
274  list_for_each_entry(voltdm, &voltdm_list, node) {
275  struct clk *sys_ck;
276 
277  if (!voltdm->scalable)
278  continue;
279 
280  sys_ck = clk_get(NULL, voltdm->sys_clk.name);
281  if (IS_ERR(sys_ck)) {
282  pr_warning("%s: Could not get sys clk.\n", __func__);
283  return -EINVAL;
284  }
285  voltdm->sys_clk.rate = clk_get_rate(sys_ck);
286  WARN_ON(!voltdm->sys_clk.rate);
287  clk_put(sys_ck);
288 
289  if (voltdm->vc) {
290  voltdm->scale = omap_vc_bypass_scale;
291  omap_vc_init_channel(voltdm);
292  }
293 
294  if (voltdm->vp) {
296  omap_vp_init(voltdm);
297  }
298  }
299 
300  return 0;
301 }
302 
303 static struct voltagedomain *_voltdm_lookup(const char *name)
304 {
305  struct voltagedomain *voltdm, *temp_voltdm;
306 
307  voltdm = NULL;
308 
309  list_for_each_entry(temp_voltdm, &voltdm_list, node) {
310  if (!strcmp(name, temp_voltdm->name)) {
311  voltdm = temp_voltdm;
312  break;
313  }
314  }
315 
316  return voltdm;
317 }
318 
329 int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm)
330 {
331  if (!voltdm || !pwrdm)
332  return -EINVAL;
333 
334  pr_debug("voltagedomain: %s: associating powerdomain %s\n",
335  voltdm->name, pwrdm->name);
336 
337  list_add(&pwrdm->voltdm_node, &voltdm->pwrdm_list);
338 
339  return 0;
340 }
341 
354  int (*fn)(struct voltagedomain *voltdm,
355  struct powerdomain *pwrdm))
356 {
357  struct powerdomain *pwrdm;
358  int ret = 0;
359 
360  if (!fn)
361  return -EINVAL;
362 
363  list_for_each_entry(pwrdm, &voltdm->pwrdm_list, voltdm_node)
364  ret = (*fn)(voltdm, pwrdm);
365 
366  return ret;
367 }
368 
379 int voltdm_for_each(int (*fn)(struct voltagedomain *voltdm, void *user),
380  void *user)
381 {
382  struct voltagedomain *temp_voltdm;
383  int ret = 0;
384 
385  if (!fn)
386  return -EINVAL;
387 
388  list_for_each_entry(temp_voltdm, &voltdm_list, node) {
389  ret = (*fn)(temp_voltdm, user);
390  if (ret)
391  break;
392  }
393 
394  return ret;
395 }
396 
397 static int _voltdm_register(struct voltagedomain *voltdm)
398 {
399  if (!voltdm || !voltdm->name)
400  return -EINVAL;
401 
402  INIT_LIST_HEAD(&voltdm->pwrdm_list);
403  list_add(&voltdm->node, &voltdm_list);
404 
405  pr_debug("voltagedomain: registered %s\n", voltdm->name);
406 
407  return 0;
408 }
409 
417 struct voltagedomain *voltdm_lookup(const char *name)
418 {
419  struct voltagedomain *voltdm ;
420 
421  if (!name)
422  return NULL;
423 
424  voltdm = _voltdm_lookup(name);
425 
426  return voltdm;
427 }
428 
438 void voltdm_init(struct voltagedomain **voltdms)
439 {
440  struct voltagedomain **v;
441 
442  if (voltdms) {
443  for (v = voltdms; *v; v++)
444  _voltdm_register(*v);
445  }
446 }