voltage.c (8622B)
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}