Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Utility functions for parsing Tegra CVB voltage tables
0004  *
0005  * Copyright (C) 2012-2019 NVIDIA Corporation.  All rights reserved.
0006  */
0007 #include <linux/err.h>
0008 #include <linux/kernel.h>
0009 #include <linux/pm_opp.h>
0010 
0011 #include "cvb.h"
0012 
0013 /* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
0014 static inline int get_cvb_voltage(int speedo, int s_scale,
0015                   const struct cvb_coefficients *cvb)
0016 {
0017     int mv;
0018 
0019     /* apply only speedo scale: output mv = cvb_mv * v_scale */
0020     mv = DIV_ROUND_CLOSEST(cvb->c2 * speedo, s_scale);
0021     mv = DIV_ROUND_CLOSEST((mv + cvb->c1) * speedo, s_scale) + cvb->c0;
0022     return mv;
0023 }
0024 
0025 static int round_cvb_voltage(int mv, int v_scale,
0026                  const struct rail_alignment *align)
0027 {
0028     /* combined: apply voltage scale and round to cvb alignment step */
0029     int uv;
0030     int step = (align->step_uv ? : 1000) * v_scale;
0031     int offset = align->offset_uv * v_scale;
0032 
0033     uv = max(mv * 1000, offset) - offset;
0034     uv = DIV_ROUND_UP(uv, step) * align->step_uv + align->offset_uv;
0035     return uv / 1000;
0036 }
0037 
0038 enum {
0039     DOWN,
0040     UP
0041 };
0042 
0043 static int round_voltage(int mv, const struct rail_alignment *align, int up)
0044 {
0045     if (align->step_uv) {
0046         int uv;
0047 
0048         uv = max(mv * 1000, align->offset_uv) - align->offset_uv;
0049         uv = (uv + (up ? align->step_uv - 1 : 0)) / align->step_uv;
0050         return (uv * align->step_uv + align->offset_uv) / 1000;
0051     }
0052     return mv;
0053 }
0054 
0055 static int build_opp_table(struct device *dev, const struct cvb_table *table,
0056                struct rail_alignment *align,
0057                int speedo_value, unsigned long max_freq)
0058 {
0059     int i, ret, dfll_mv, min_mv, max_mv;
0060 
0061     min_mv = round_voltage(table->min_millivolts, align, UP);
0062     max_mv = round_voltage(table->max_millivolts, align, DOWN);
0063 
0064     for (i = 0; i < MAX_DVFS_FREQS; i++) {
0065         const struct cvb_table_freq_entry *entry = &table->entries[i];
0066 
0067         if (!entry->freq || (entry->freq > max_freq))
0068             break;
0069 
0070         dfll_mv = get_cvb_voltage(speedo_value, table->speedo_scale,
0071                       &entry->coefficients);
0072         dfll_mv = round_cvb_voltage(dfll_mv, table->voltage_scale,
0073                         align);
0074         dfll_mv = clamp(dfll_mv, min_mv, max_mv);
0075 
0076         ret = dev_pm_opp_add(dev, entry->freq, dfll_mv * 1000);
0077         if (ret)
0078             return ret;
0079     }
0080 
0081     return 0;
0082 }
0083 
0084 /**
0085  * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables
0086  * @dev: the struct device * for which the OPP table is built
0087  * @tables: array of CVB tables
0088  * @count: size of the previously mentioned array
0089  * @align: parameters of the regulator step and offset
0090  * @process_id: process id of the HW module
0091  * @speedo_id: speedo id of the HW module
0092  * @speedo_value: speedo value of the HW module
0093  * @max_freq: highest safe clock rate
0094  *
0095  * On Tegra, a CVB table encodes the relationship between operating voltage
0096  * and safe maximal frequency for a given module (e.g. GPU or CPU). This
0097  * function calculates the optimal voltage-frequency operating points
0098  * for the given arguments and exports them via the OPP library for the
0099  * given @dev. Returns a pointer to the struct cvb_table that matched
0100  * or an ERR_PTR on failure.
0101  */
0102 const struct cvb_table *
0103 tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
0104             size_t count, struct rail_alignment *align,
0105             int process_id, int speedo_id, int speedo_value,
0106             unsigned long max_freq)
0107 {
0108     size_t i;
0109     int ret;
0110 
0111     for (i = 0; i < count; i++) {
0112         const struct cvb_table *table = &tables[i];
0113 
0114         if (table->speedo_id != -1 && table->speedo_id != speedo_id)
0115             continue;
0116 
0117         if (table->process_id != -1 && table->process_id != process_id)
0118             continue;
0119 
0120         ret = build_opp_table(dev, table, align, speedo_value,
0121                       max_freq);
0122         return ret ? ERR_PTR(ret) : table;
0123     }
0124 
0125     return ERR_PTR(-EINVAL);
0126 }
0127 
0128 void tegra_cvb_remove_opp_table(struct device *dev,
0129                 const struct cvb_table *table,
0130                 unsigned long max_freq)
0131 {
0132     unsigned int i;
0133 
0134     for (i = 0; i < MAX_DVFS_FREQS; i++) {
0135         const struct cvb_table_freq_entry *entry = &table->entries[i];
0136 
0137         if (!entry->freq || (entry->freq > max_freq))
0138             break;
0139 
0140         dev_pm_opp_remove(dev, entry->freq);
0141     }
0142 }