root/arch/arm/mach-omap2/voltage.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. voltdm_get_voltage
  2. voltdm_scale
  3. voltdm_reset
  4. omap_voltage_get_volttable
  5. omap_voltage_get_voltdata
  6. omap_voltage_register_pmic
  7. omap_voltage_late_init
  8. _voltdm_lookup
  9. _voltdm_register
  10. voltdm_lookup
  11. voltdm_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * OMAP3/OMAP4 Voltage Management Routines
   4  *
   5  * Author: Thara Gopinath       <thara@ti.com>
   6  *
   7  * Copyright (C) 2007 Texas Instruments, Inc.
   8  * Rajendra Nayak <rnayak@ti.com>
   9  * Lesly A M <x0080970@ti.com>
  10  *
  11  * Copyright (C) 2008, 2011 Nokia Corporation
  12  * Kalle Jokiniemi
  13  * Paul Walmsley
  14  *
  15  * Copyright (C) 2010 Texas Instruments, Inc.
  16  * Thara Gopinath <thara@ti.com>
  17  */
  18 
  19 #include <linux/delay.h>
  20 #include <linux/io.h>
  21 #include <linux/err.h>
  22 #include <linux/export.h>
  23 #include <linux/debugfs.h>
  24 #include <linux/slab.h>
  25 #include <linux/clk.h>
  26 
  27 #include "common.h"
  28 
  29 #include "prm-regbits-34xx.h"
  30 #include "prm-regbits-44xx.h"
  31 #include "prm44xx.h"
  32 #include "prcm44xx.h"
  33 #include "prminst44xx.h"
  34 #include "control.h"
  35 
  36 #include "voltage.h"
  37 #include "powerdomain.h"
  38 
  39 #include "vc.h"
  40 #include "vp.h"
  41 
  42 static LIST_HEAD(voltdm_list);
  43 
  44 /* Public functions */
  45 /**
  46  * voltdm_get_voltage() - Gets the current non-auto-compensated voltage
  47  * @voltdm:     pointer to the voltdm for which current voltage info is needed
  48  *
  49  * API to get the current non-auto-compensated voltage for a voltage domain.
  50  * Returns 0 in case of error else returns the current voltage.
  51  */
  52 unsigned long voltdm_get_voltage(struct voltagedomain *voltdm)
  53 {
  54         if (!voltdm || IS_ERR(voltdm)) {
  55                 pr_warn("%s: VDD specified does not exist!\n", __func__);
  56                 return 0;
  57         }
  58 
  59         return voltdm->nominal_volt;
  60 }
  61 
  62 /**
  63  * voltdm_scale() - API to scale voltage of a particular voltage domain.
  64  * @voltdm: pointer to the voltage domain which is to be scaled.
  65  * @target_volt: The target voltage of the voltage domain
  66  *
  67  * This API should be called by the kernel to do the voltage scaling
  68  * for a particular voltage domain during DVFS.
  69  */
  70 int voltdm_scale(struct voltagedomain *voltdm,
  71                  unsigned long target_volt)
  72 {
  73         int ret, i;
  74         unsigned long volt = 0;
  75 
  76         if (!voltdm || IS_ERR(voltdm)) {
  77                 pr_warn("%s: VDD specified does not exist!\n", __func__);
  78                 return -EINVAL;
  79         }
  80 
  81         if (!voltdm->scale) {
  82                 pr_err("%s: No voltage scale API registered for vdd_%s\n",
  83                         __func__, voltdm->name);
  84                 return -ENODATA;
  85         }
  86 
  87         if (!voltdm->volt_data) {
  88                 pr_err("%s: No voltage data defined for vdd_%s\n",
  89                         __func__, voltdm->name);
  90                 return -ENODATA;
  91         }
  92 
  93         /* Adjust voltage to the exact voltage from the OPP table */
  94         for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
  95                 if (voltdm->volt_data[i].volt_nominal >= target_volt) {
  96                         volt = voltdm->volt_data[i].volt_nominal;
  97                         break;
  98                 }
  99         }
 100 
 101         if (!volt) {
 102                 pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n",
 103                         __func__, target_volt);
 104                 return -EINVAL;
 105         }
 106 
 107         ret = voltdm->scale(voltdm, volt);
 108         if (!ret)
 109                 voltdm->nominal_volt = volt;
 110 
 111         return ret;
 112 }
 113 
 114 /**
 115  * voltdm_reset() - Resets the voltage of a particular voltage domain
 116  *                  to that of the current OPP.
 117  * @voltdm: pointer to the voltage domain whose voltage is to be reset.
 118  *
 119  * This API finds out the correct voltage the voltage domain is supposed
 120  * to be at and resets the voltage to that level. Should be used especially
 121  * while disabling any voltage compensation modules.
 122  */
 123 void voltdm_reset(struct voltagedomain *voltdm)
 124 {
 125         unsigned long target_volt;
 126 
 127         if (!voltdm || IS_ERR(voltdm)) {
 128                 pr_warn("%s: VDD specified does not exist!\n", __func__);
 129                 return;
 130         }
 131 
 132         target_volt = voltdm_get_voltage(voltdm);
 133         if (!target_volt) {
 134                 pr_err("%s: unable to find current voltage for vdd_%s\n",
 135                         __func__, voltdm->name);
 136                 return;
 137         }
 138 
 139         voltdm_scale(voltdm, target_volt);
 140 }
 141 
 142 /**
 143  * omap_voltage_get_volttable() - API to get the voltage table associated with a
 144  *                              particular voltage domain.
 145  * @voltdm:     pointer to the VDD for which the voltage table is required
 146  * @volt_data:  the voltage table for the particular vdd which is to be
 147  *              populated by this API
 148  *
 149  * This API populates the voltage table associated with a VDD into the
 150  * passed parameter pointer. Returns the count of distinct voltages
 151  * supported by this vdd.
 152  *
 153  */
 154 void omap_voltage_get_volttable(struct voltagedomain *voltdm,
 155                                 struct omap_volt_data **volt_data)
 156 {
 157         if (!voltdm || IS_ERR(voltdm)) {
 158                 pr_warn("%s: VDD specified does not exist!\n", __func__);
 159                 return;
 160         }
 161 
 162         *volt_data = voltdm->volt_data;
 163 }
 164 
 165 /**
 166  * omap_voltage_get_voltdata() - API to get the voltage table entry for a
 167  *                              particular voltage
 168  * @voltdm:     pointer to the VDD whose voltage table has to be searched
 169  * @volt:       the voltage to be searched in the voltage table
 170  *
 171  * This API searches through the voltage table for the required voltage
 172  * domain and tries to find a matching entry for the passed voltage volt.
 173  * If a matching entry is found volt_data is populated with that entry.
 174  * This API searches only through the non-compensated voltages int the
 175  * voltage table.
 176  * Returns pointer to the voltage table entry corresponding to volt on
 177  * success. Returns -ENODATA if no voltage table exisits for the passed voltage
 178  * domain or if there is no matching entry.
 179  */
 180 struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
 181                                                  unsigned long volt)
 182 {
 183         int i;
 184 
 185         if (!voltdm || IS_ERR(voltdm)) {
 186                 pr_warn("%s: VDD specified does not exist!\n", __func__);
 187                 return ERR_PTR(-EINVAL);
 188         }
 189 
 190         if (!voltdm->volt_data) {
 191                 pr_warn("%s: voltage table does not exist for vdd_%s\n",
 192                         __func__, voltdm->name);
 193                 return ERR_PTR(-ENODATA);
 194         }
 195 
 196         for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
 197                 if (voltdm->volt_data[i].volt_nominal == volt)
 198                         return &voltdm->volt_data[i];
 199         }
 200 
 201         pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n",
 202                   __func__, voltdm->name);
 203 
 204         return ERR_PTR(-ENODATA);
 205 }
 206 
 207 /**
 208  * omap_voltage_register_pmic() - API to register PMIC specific data
 209  * @voltdm:     pointer to the VDD for which the PMIC specific data is
 210  *              to be registered
 211  * @pmic:       the structure containing pmic info
 212  *
 213  * This API is to be called by the SOC/PMIC file to specify the
 214  * pmic specific info as present in omap_voltdm_pmic structure.
 215  */
 216 int omap_voltage_register_pmic(struct voltagedomain *voltdm,
 217                                struct omap_voltdm_pmic *pmic)
 218 {
 219         if (!voltdm || IS_ERR(voltdm)) {
 220                 pr_warn("%s: VDD specified does not exist!\n", __func__);
 221                 return -EINVAL;
 222         }
 223 
 224         voltdm->pmic = pmic;
 225 
 226         return 0;
 227 }
 228 
 229 /**
 230  * omap_voltage_late_init() - Init the various voltage parameters
 231  *
 232  * This API is to be called in the later stages of the
 233  * system boot to init the voltage controller and
 234  * voltage processors.
 235  */
 236 int __init omap_voltage_late_init(void)
 237 {
 238         struct voltagedomain *voltdm;
 239 
 240         if (list_empty(&voltdm_list)) {
 241                 pr_err("%s: Voltage driver support not added\n",
 242                         __func__);
 243                 return -EINVAL;
 244         }
 245 
 246         list_for_each_entry(voltdm, &voltdm_list, node) {
 247                 struct clk *sys_ck;
 248 
 249                 if (!voltdm->scalable)
 250                         continue;
 251 
 252                 sys_ck = clk_get(NULL, voltdm->sys_clk.name);
 253                 if (IS_ERR(sys_ck)) {
 254                         pr_warn("%s: Could not get sys clk.\n", __func__);
 255                         return -EINVAL;
 256                 }
 257                 voltdm->sys_clk.rate = clk_get_rate(sys_ck);
 258                 WARN_ON(!voltdm->sys_clk.rate);
 259                 clk_put(sys_ck);
 260 
 261                 if (voltdm->vc) {
 262                         voltdm->scale = omap_vc_bypass_scale;
 263                         omap_vc_init_channel(voltdm);
 264                 }
 265 
 266                 if (voltdm->vp) {
 267                         voltdm->scale = omap_vp_forceupdate_scale;
 268                         omap_vp_init(voltdm);
 269                 }
 270         }
 271 
 272         return 0;
 273 }
 274 
 275 static struct voltagedomain *_voltdm_lookup(const char *name)
 276 {
 277         struct voltagedomain *voltdm, *temp_voltdm;
 278 
 279         voltdm = NULL;
 280 
 281         list_for_each_entry(temp_voltdm, &voltdm_list, node) {
 282                 if (!strcmp(name, temp_voltdm->name)) {
 283                         voltdm = temp_voltdm;
 284                         break;
 285                 }
 286         }
 287 
 288         return voltdm;
 289 }
 290 
 291 static int _voltdm_register(struct voltagedomain *voltdm)
 292 {
 293         if (!voltdm || !voltdm->name)
 294                 return -EINVAL;
 295 
 296         list_add(&voltdm->node, &voltdm_list);
 297 
 298         pr_debug("voltagedomain: registered %s\n", voltdm->name);
 299 
 300         return 0;
 301 }
 302 
 303 /**
 304  * voltdm_lookup - look up a voltagedomain by name, return a pointer
 305  * @name: name of voltagedomain
 306  *
 307  * Find a registered voltagedomain by its name @name.  Returns a pointer
 308  * to the struct voltagedomain if found, or NULL otherwise.
 309  */
 310 struct voltagedomain *voltdm_lookup(const char *name)
 311 {
 312         struct voltagedomain *voltdm ;
 313 
 314         if (!name)
 315                 return NULL;
 316 
 317         voltdm = _voltdm_lookup(name);
 318 
 319         return voltdm;
 320 }
 321 
 322 /**
 323  * voltdm_init - set up the voltagedomain layer
 324  * @voltdm_list: array of struct voltagedomain pointers to register
 325  *
 326  * Loop through the array of voltagedomains @voltdm_list, registering all
 327  * that are available on the current CPU. If voltdm_list is supplied
 328  * and not null, all of the referenced voltagedomains will be
 329  * registered.  No return value.
 330  */
 331 void voltdm_init(struct voltagedomain **voltdms)
 332 {
 333         struct voltagedomain **v;
 334 
 335         if (voltdms) {
 336                 for (v = voltdms; *v; v++)
 337                         _voltdm_register(*v);
 338         }
 339 }

/* [<][>][^][v][top][bottom][index][help] */