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