linux/arch/arm/mach-omap2/voltage.c
<<
>>
Prefs
   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 *
  10 * Copyright (C) 2008, 2011 Nokia Corporation
  11 * Kalle Jokiniemi
  12 * Paul Walmsley
  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/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
  45static LIST_HEAD(voltdm_list);
  46
  47/* Public functions */
  48/**
  49 * voltdm_get_voltage() - Gets the current non-auto-compensated voltage
  50 * @voltdm:     pointer to the voltdm for which current voltage info is needed
  51 *
  52 * API to get the current non-auto-compensated voltage for a voltage domain.
  53 * Returns 0 in case of error else returns the current voltage.
  54 */
  55unsigned 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
  65/**
  66 * voltdm_scale() - API to scale voltage of a particular voltage domain.
  67 * @voltdm: pointer to the voltage domain which is to be scaled.
  68 * @target_volt: The target voltage of the voltage domain
  69 *
  70 * This API should be called by the kernel to do the voltage scaling
  71 * for a particular voltage domain during DVFS.
  72 */
  73int 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
 111/**
 112 * voltdm_reset() - Resets the voltage of a particular voltage domain
 113 *                  to that of the current OPP.
 114 * @voltdm: pointer to the voltage domain whose voltage is to be reset.
 115 *
 116 * This API finds out the correct voltage the voltage domain is supposed
 117 * to be at and resets the voltage to that level. Should be used especially
 118 * while disabling any voltage compensation modules.
 119 */
 120void 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
 139/**
 140 * omap_voltage_get_volttable() - API to get the voltage table associated with a
 141 *                              particular voltage domain.
 142 * @voltdm:     pointer to the VDD for which the voltage table is required
 143 * @volt_data:  the voltage table for the particular vdd which is to be
 144 *              populated by this API
 145 *
 146 * This API populates the voltage table associated with a VDD into the
 147 * passed parameter pointer. Returns the count of distinct voltages
 148 * supported by this vdd.
 149 *
 150 */
 151void omap_voltage_get_volttable(struct voltagedomain *voltdm,
 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
 162/**
 163 * omap_voltage_get_voltdata() - API to get the voltage table entry for a
 164 *                              particular voltage
 165 * @voltdm:     pointer to the VDD whose voltage table has to be searched
 166 * @volt:       the voltage to be searched in the voltage table
 167 *
 168 * This API searches through the voltage table for the required voltage
 169 * domain and tries to find a matching entry for the passed voltage volt.
 170 * If a matching entry is found volt_data is populated with that entry.
 171 * This API searches only through the non-compensated voltages int the
 172 * voltage table.
 173 * Returns pointer to the voltage table entry corresponding to volt on
 174 * success. Returns -ENODATA if no voltage table exisits for the passed voltage
 175 * domain or if there is no matching entry.
 176 */
 177struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
 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
 204/**
 205 * omap_voltage_register_pmic() - API to register PMIC specific data
 206 * @voltdm:     pointer to the VDD for which the PMIC specific data is
 207 *              to be registered
 208 * @pmic:       the structure containing pmic info
 209 *
 210 * This API is to be called by the SOC/PMIC file to specify the
 211 * pmic specific info as present in omap_voltdm_pmic structure.
 212 */
 213int omap_voltage_register_pmic(struct voltagedomain *voltdm,
 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
 226/**
 227 * omap_change_voltscale_method() - API to change the voltage scaling method.
 228 * @voltdm:     pointer to the VDD whose voltage scaling method
 229 *              has to be changed.
 230 * @voltscale_method:   the method to be used for voltage scaling.
 231 *
 232 * This API can be used by the board files to change the method of voltage
 233 * scaling between vpforceupdate and vcbypass. The parameter values are
 234 * defined in voltage.h
 235 */
 236void omap_change_voltscale_method(struct voltagedomain *voltdm,
 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) {
 245        case VOLTSCALE_VPFORCEUPDATE:
 246                voltdm->scale = omap_vp_forceupdate_scale;
 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
 257/**
 258 * omap_voltage_late_init() - Init the various voltage parameters
 259 *
 260 * This API is to be called in the later stages of the
 261 * system boot to init the voltage controller and
 262 * voltage processors.
 263 */
 264int __init omap_voltage_late_init(void)
 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) {
 295                        voltdm->scale = omap_vp_forceupdate_scale;
 296                        omap_vp_init(voltdm);
 297                }
 298        }
 299
 300        return 0;
 301}
 302
 303static 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
 319/**
 320 * voltdm_add_pwrdm - add a powerdomain to a voltagedomain
 321 * @voltdm: struct voltagedomain * to add the powerdomain to
 322 * @pwrdm: struct powerdomain * to associate with a voltagedomain
 323 *
 324 * Associate the powerdomain @pwrdm with a voltagedomain @voltdm.  This
 325 * enables the use of voltdm_for_each_pwrdm().  Returns -EINVAL if
 326 * presented with invalid pointers; -ENOMEM if memory could not be allocated;
 327 * or 0 upon success.
 328 */
 329int 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
 342/**
 343 * voltdm_for_each_pwrdm - call function for each pwrdm in a voltdm
 344 * @voltdm: struct voltagedomain * to iterate over
 345 * @fn: callback function *
 346 *
 347 * Call the supplied function @fn for each powerdomain in the
 348 * voltagedomain @voltdm.  Returns -EINVAL if presented with invalid
 349 * pointers; or passes along the last return value of the callback
 350 * function, which should be 0 for success or anything else to
 351 * indicate failure.
 352 */
 353int voltdm_for_each_pwrdm(struct voltagedomain *voltdm,
 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
 369/**
 370 * voltdm_for_each - call function on each registered voltagedomain
 371 * @fn: callback function *
 372 *
 373 * Call the supplied function @fn for each registered voltagedomain.
 374 * The callback function @fn can return anything but 0 to bail out
 375 * early from the iterator.  Returns the last return value of the
 376 * callback function, which should be 0 for success or anything else
 377 * to indicate failure; or -EINVAL if the function pointer is null.
 378 */
 379int 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
 397static 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
 410/**
 411 * voltdm_lookup - look up a voltagedomain by name, return a pointer
 412 * @name: name of voltagedomain
 413 *
 414 * Find a registered voltagedomain by its name @name.  Returns a pointer
 415 * to the struct voltagedomain if found, or NULL otherwise.
 416 */
 417struct 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
 429/**
 430 * voltdm_init - set up the voltagedomain layer
 431 * @voltdm_list: array of struct voltagedomain pointers to register
 432 *
 433 * Loop through the array of voltagedomains @voltdm_list, registering all
 434 * that are available on the current CPU. If voltdm_list is supplied
 435 * and not null, all of the referenced voltagedomains will be
 436 * registered.  No return value.
 437 */
 438void 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}
 447