linux/arch/arm/mach-omap2/voltage.c
<<
>>
Prefs
   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
  42static 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 */
  52unsigned 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 */
  70int 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 */
 123void 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 */
 154void 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 */
 180struct 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 */
 216int 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 */
 236int __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
 275static 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
 291static 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 */
 310struct 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 */
 331void 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}
 340