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_warn("%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_warn("%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_warn("%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_warn("%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_warn("%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_warn("%s: VDD specified does not exist!\n", __func__);
 184                return ERR_PTR(-EINVAL);
 185        }
 186
 187        if (!voltdm->volt_data) {
 188                pr_warn("%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_warn("%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_voltage_late_init() - Init the various voltage parameters
 228 *
 229 * This API is to be called in the later stages of the
 230 * system boot to init the voltage controller and
 231 * voltage processors.
 232 */
 233int __init omap_voltage_late_init(void)
 234{
 235        struct voltagedomain *voltdm;
 236
 237        if (list_empty(&voltdm_list)) {
 238                pr_err("%s: Voltage driver support not added\n",
 239                        __func__);
 240                return -EINVAL;
 241        }
 242
 243        list_for_each_entry(voltdm, &voltdm_list, node) {
 244                struct clk *sys_ck;
 245
 246                if (!voltdm->scalable)
 247                        continue;
 248
 249                sys_ck = clk_get(NULL, voltdm->sys_clk.name);
 250                if (IS_ERR(sys_ck)) {
 251                        pr_warn("%s: Could not get sys clk.\n", __func__);
 252                        return -EINVAL;
 253                }
 254                voltdm->sys_clk.rate = clk_get_rate(sys_ck);
 255                WARN_ON(!voltdm->sys_clk.rate);
 256                clk_put(sys_ck);
 257
 258                if (voltdm->vc) {
 259                        voltdm->scale = omap_vc_bypass_scale;
 260                        omap_vc_init_channel(voltdm);
 261                }
 262
 263                if (voltdm->vp) {
 264                        voltdm->scale = omap_vp_forceupdate_scale;
 265                        omap_vp_init(voltdm);
 266                }
 267        }
 268
 269        return 0;
 270}
 271
 272static struct voltagedomain *_voltdm_lookup(const char *name)
 273{
 274        struct voltagedomain *voltdm, *temp_voltdm;
 275
 276        voltdm = NULL;
 277
 278        list_for_each_entry(temp_voltdm, &voltdm_list, node) {
 279                if (!strcmp(name, temp_voltdm->name)) {
 280                        voltdm = temp_voltdm;
 281                        break;
 282                }
 283        }
 284
 285        return voltdm;
 286}
 287
 288static int _voltdm_register(struct voltagedomain *voltdm)
 289{
 290        if (!voltdm || !voltdm->name)
 291                return -EINVAL;
 292
 293        list_add(&voltdm->node, &voltdm_list);
 294
 295        pr_debug("voltagedomain: registered %s\n", voltdm->name);
 296
 297        return 0;
 298}
 299
 300/**
 301 * voltdm_lookup - look up a voltagedomain by name, return a pointer
 302 * @name: name of voltagedomain
 303 *
 304 * Find a registered voltagedomain by its name @name.  Returns a pointer
 305 * to the struct voltagedomain if found, or NULL otherwise.
 306 */
 307struct voltagedomain *voltdm_lookup(const char *name)
 308{
 309        struct voltagedomain *voltdm ;
 310
 311        if (!name)
 312                return NULL;
 313
 314        voltdm = _voltdm_lookup(name);
 315
 316        return voltdm;
 317}
 318
 319/**
 320 * voltdm_init - set up the voltagedomain layer
 321 * @voltdm_list: array of struct voltagedomain pointers to register
 322 *
 323 * Loop through the array of voltagedomains @voltdm_list, registering all
 324 * that are available on the current CPU. If voltdm_list is supplied
 325 * and not null, all of the referenced voltagedomains will be
 326 * registered.  No return value.
 327 */
 328void voltdm_init(struct voltagedomain **voltdms)
 329{
 330        struct voltagedomain **v;
 331
 332        if (voltdms) {
 333                for (v = voltdms; *v; v++)
 334                        _voltdm_register(*v);
 335        }
 336}
 337