Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * OMAP3/OMAP4 Voltage Management Routines
0004  *
0005  * Author: Thara Gopinath   <thara@ti.com>
0006  *
0007  * Copyright (C) 2007 Texas Instruments, Inc.
0008  * Rajendra Nayak <rnayak@ti.com>
0009  * Lesly A M <x0080970@ti.com>
0010  *
0011  * Copyright (C) 2008, 2011 Nokia Corporation
0012  * Kalle Jokiniemi
0013  * Paul Walmsley
0014  *
0015  * Copyright (C) 2010 Texas Instruments, Inc.
0016  * Thara Gopinath <thara@ti.com>
0017  */
0018 
0019 #include <linux/delay.h>
0020 #include <linux/io.h>
0021 #include <linux/err.h>
0022 #include <linux/export.h>
0023 #include <linux/debugfs.h>
0024 #include <linux/slab.h>
0025 #include <linux/clk.h>
0026 
0027 #include "common.h"
0028 
0029 #include "prm-regbits-34xx.h"
0030 #include "prm-regbits-44xx.h"
0031 #include "prm44xx.h"
0032 #include "prcm44xx.h"
0033 #include "prminst44xx.h"
0034 #include "control.h"
0035 
0036 #include "voltage.h"
0037 #include "powerdomain.h"
0038 
0039 #include "vc.h"
0040 #include "vp.h"
0041 
0042 static LIST_HEAD(voltdm_list);
0043 
0044 /* Public functions */
0045 /**
0046  * voltdm_get_voltage() - Gets the current non-auto-compensated voltage
0047  * @voltdm: pointer to the voltdm for which current voltage info is needed
0048  *
0049  * API to get the current non-auto-compensated voltage for a voltage domain.
0050  * Returns 0 in case of error else returns the current voltage.
0051  */
0052 unsigned long voltdm_get_voltage(struct voltagedomain *voltdm)
0053 {
0054     if (!voltdm || IS_ERR(voltdm)) {
0055         pr_warn("%s: VDD specified does not exist!\n", __func__);
0056         return 0;
0057     }
0058 
0059     return voltdm->nominal_volt;
0060 }
0061 
0062 /**
0063  * voltdm_scale() - API to scale voltage of a particular voltage domain.
0064  * @voltdm: pointer to the voltage domain which is to be scaled.
0065  * @target_volt: The target voltage of the voltage domain
0066  *
0067  * This API should be called by the kernel to do the voltage scaling
0068  * for a particular voltage domain during DVFS.
0069  */
0070 int voltdm_scale(struct voltagedomain *voltdm,
0071          unsigned long target_volt)
0072 {
0073     int ret, i;
0074     unsigned long volt = 0;
0075 
0076     if (!voltdm || IS_ERR(voltdm)) {
0077         pr_warn("%s: VDD specified does not exist!\n", __func__);
0078         return -EINVAL;
0079     }
0080 
0081     if (!voltdm->scale) {
0082         pr_err("%s: No voltage scale API registered for vdd_%s\n",
0083             __func__, voltdm->name);
0084         return -ENODATA;
0085     }
0086 
0087     if (!voltdm->volt_data) {
0088         pr_err("%s: No voltage data defined for vdd_%s\n",
0089             __func__, voltdm->name);
0090         return -ENODATA;
0091     }
0092 
0093     /* Adjust voltage to the exact voltage from the OPP table */
0094     for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
0095         if (voltdm->volt_data[i].volt_nominal >= target_volt) {
0096             volt = voltdm->volt_data[i].volt_nominal;
0097             break;
0098         }
0099     }
0100 
0101     if (!volt) {
0102         pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n",
0103             __func__, target_volt);
0104         return -EINVAL;
0105     }
0106 
0107     ret = voltdm->scale(voltdm, volt);
0108     if (!ret)
0109         voltdm->nominal_volt = volt;
0110 
0111     return ret;
0112 }
0113 
0114 /**
0115  * voltdm_reset() - Resets the voltage of a particular voltage domain
0116  *          to that of the current OPP.
0117  * @voltdm: pointer to the voltage domain whose voltage is to be reset.
0118  *
0119  * This API finds out the correct voltage the voltage domain is supposed
0120  * to be at and resets the voltage to that level. Should be used especially
0121  * while disabling any voltage compensation modules.
0122  */
0123 void voltdm_reset(struct voltagedomain *voltdm)
0124 {
0125     unsigned long target_volt;
0126 
0127     if (!voltdm || IS_ERR(voltdm)) {
0128         pr_warn("%s: VDD specified does not exist!\n", __func__);
0129         return;
0130     }
0131 
0132     target_volt = voltdm_get_voltage(voltdm);
0133     if (!target_volt) {
0134         pr_err("%s: unable to find current voltage for vdd_%s\n",
0135             __func__, voltdm->name);
0136         return;
0137     }
0138 
0139     voltdm_scale(voltdm, target_volt);
0140 }
0141 
0142 /**
0143  * omap_voltage_get_volttable() - API to get the voltage table associated with a
0144  *              particular voltage domain.
0145  * @voltdm: pointer to the VDD for which the voltage table is required
0146  * @volt_data:  the voltage table for the particular vdd which is to be
0147  *      populated by this API
0148  *
0149  * This API populates the voltage table associated with a VDD into the
0150  * passed parameter pointer. Returns the count of distinct voltages
0151  * supported by this vdd.
0152  *
0153  */
0154 void omap_voltage_get_volttable(struct voltagedomain *voltdm,
0155                 struct omap_volt_data **volt_data)
0156 {
0157     if (!voltdm || IS_ERR(voltdm)) {
0158         pr_warn("%s: VDD specified does not exist!\n", __func__);
0159         return;
0160     }
0161 
0162     *volt_data = voltdm->volt_data;
0163 }
0164 
0165 /**
0166  * omap_voltage_get_voltdata() - API to get the voltage table entry for a
0167  *              particular voltage
0168  * @voltdm: pointer to the VDD whose voltage table has to be searched
0169  * @volt:   the voltage to be searched in the voltage table
0170  *
0171  * This API searches through the voltage table for the required voltage
0172  * domain and tries to find a matching entry for the passed voltage volt.
0173  * If a matching entry is found volt_data is populated with that entry.
0174  * This API searches only through the non-compensated voltages int the
0175  * voltage table.
0176  * Returns pointer to the voltage table entry corresponding to volt on
0177  * success. Returns -ENODATA if no voltage table exisits for the passed voltage
0178  * domain or if there is no matching entry.
0179  */
0180 struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
0181                          unsigned long volt)
0182 {
0183     int i;
0184 
0185     if (!voltdm || IS_ERR(voltdm)) {
0186         pr_warn("%s: VDD specified does not exist!\n", __func__);
0187         return ERR_PTR(-EINVAL);
0188     }
0189 
0190     if (!voltdm->volt_data) {
0191         pr_warn("%s: voltage table does not exist for vdd_%s\n",
0192             __func__, voltdm->name);
0193         return ERR_PTR(-ENODATA);
0194     }
0195 
0196     for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
0197         if (voltdm->volt_data[i].volt_nominal == volt)
0198             return &voltdm->volt_data[i];
0199     }
0200 
0201     pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n",
0202           __func__, voltdm->name);
0203 
0204     return ERR_PTR(-ENODATA);
0205 }
0206 
0207 /**
0208  * omap_voltage_register_pmic() - API to register PMIC specific data
0209  * @voltdm: pointer to the VDD for which the PMIC specific data is
0210  *      to be registered
0211  * @pmic:   the structure containing pmic info
0212  *
0213  * This API is to be called by the SOC/PMIC file to specify the
0214  * pmic specific info as present in omap_voltdm_pmic structure.
0215  */
0216 int omap_voltage_register_pmic(struct voltagedomain *voltdm,
0217                    struct omap_voltdm_pmic *pmic)
0218 {
0219     if (!voltdm || IS_ERR(voltdm)) {
0220         pr_warn("%s: VDD specified does not exist!\n", __func__);
0221         return -EINVAL;
0222     }
0223 
0224     voltdm->pmic = pmic;
0225 
0226     return 0;
0227 }
0228 
0229 /**
0230  * omap_voltage_late_init() - Init the various voltage parameters
0231  *
0232  * This API is to be called in the later stages of the
0233  * system boot to init the voltage controller and
0234  * voltage processors.
0235  */
0236 int __init omap_voltage_late_init(void)
0237 {
0238     struct voltagedomain *voltdm;
0239 
0240     if (list_empty(&voltdm_list)) {
0241         pr_err("%s: Voltage driver support not added\n",
0242             __func__);
0243         return -EINVAL;
0244     }
0245 
0246     list_for_each_entry(voltdm, &voltdm_list, node) {
0247         struct clk *sys_ck;
0248 
0249         if (!voltdm->scalable)
0250             continue;
0251 
0252         sys_ck = clk_get(NULL, voltdm->sys_clk.name);
0253         if (IS_ERR(sys_ck)) {
0254             pr_warn("%s: Could not get sys clk.\n", __func__);
0255             return -EINVAL;
0256         }
0257         voltdm->sys_clk.rate = clk_get_rate(sys_ck);
0258         WARN_ON(!voltdm->sys_clk.rate);
0259         clk_put(sys_ck);
0260 
0261         if (voltdm->vc) {
0262             voltdm->scale = omap_vc_bypass_scale;
0263             omap_vc_init_channel(voltdm);
0264         }
0265 
0266         if (voltdm->vp) {
0267             voltdm->scale = omap_vp_forceupdate_scale;
0268             omap_vp_init(voltdm);
0269         }
0270     }
0271 
0272     return 0;
0273 }
0274 
0275 static struct voltagedomain *_voltdm_lookup(const char *name)
0276 {
0277     struct voltagedomain *voltdm, *temp_voltdm;
0278 
0279     voltdm = NULL;
0280 
0281     list_for_each_entry(temp_voltdm, &voltdm_list, node) {
0282         if (!strcmp(name, temp_voltdm->name)) {
0283             voltdm = temp_voltdm;
0284             break;
0285         }
0286     }
0287 
0288     return voltdm;
0289 }
0290 
0291 static int _voltdm_register(struct voltagedomain *voltdm)
0292 {
0293     if (!voltdm || !voltdm->name)
0294         return -EINVAL;
0295 
0296     list_add(&voltdm->node, &voltdm_list);
0297 
0298     pr_debug("voltagedomain: registered %s\n", voltdm->name);
0299 
0300     return 0;
0301 }
0302 
0303 /**
0304  * voltdm_lookup - look up a voltagedomain by name, return a pointer
0305  * @name: name of voltagedomain
0306  *
0307  * Find a registered voltagedomain by its name @name.  Returns a pointer
0308  * to the struct voltagedomain if found, or NULL otherwise.
0309  */
0310 struct voltagedomain *voltdm_lookup(const char *name)
0311 {
0312     struct voltagedomain *voltdm ;
0313 
0314     if (!name)
0315         return NULL;
0316 
0317     voltdm = _voltdm_lookup(name);
0318 
0319     return voltdm;
0320 }
0321 
0322 /**
0323  * voltdm_init - set up the voltagedomain layer
0324  * @voltdm_list: array of struct voltagedomain pointers to register
0325  *
0326  * Loop through the array of voltagedomains @voltdm_list, registering all
0327  * that are available on the current CPU. If voltdm_list is supplied
0328  * and not null, all of the referenced voltagedomains will be
0329  * registered.  No return value.
0330  */
0331 void voltdm_init(struct voltagedomain **voltdms)
0332 {
0333     struct voltagedomain **v;
0334 
0335     if (voltdms) {
0336         for (v = voltdms; *v; v++)
0337             _voltdm_register(*v);
0338     }
0339 }