2 * Utility functions for parsing Tegra CVB voltage tables
4 * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 #include <linux/err.h>
17 #include <linux/kernel.h>
18 #include <linux/pm_opp.h>
22 /* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
23 static inline int get_cvb_voltage(int speedo
, int s_scale
,
24 const struct cvb_coefficients
*cvb
)
28 /* apply only speedo scale: output mv = cvb_mv * v_scale */
29 mv
= DIV_ROUND_CLOSEST(cvb
->c2
* speedo
, s_scale
);
30 mv
= DIV_ROUND_CLOSEST((mv
+ cvb
->c1
) * speedo
, s_scale
) + cvb
->c0
;
34 static int round_cvb_voltage(int mv
, int v_scale
,
35 const struct rail_alignment
*align
)
37 /* combined: apply voltage scale and round to cvb alignment step */
39 int step
= (align
->step_uv
? : 1000) * v_scale
;
40 int offset
= align
->offset_uv
* v_scale
;
42 uv
= max(mv
* 1000, offset
) - offset
;
43 uv
= DIV_ROUND_UP(uv
, step
) * align
->step_uv
+ align
->offset_uv
;
52 static int round_voltage(int mv
, const struct rail_alignment
*align
, int up
)
57 uv
= max(mv
* 1000, align
->offset_uv
) - align
->offset_uv
;
58 uv
= (uv
+ (up
? align
->step_uv
- 1 : 0)) / align
->step_uv
;
59 return (uv
* align
->step_uv
+ align
->offset_uv
) / 1000;
64 static int build_opp_table(struct device
*dev
, const struct cvb_table
*table
,
65 int speedo_value
, unsigned long max_freq
)
67 const struct rail_alignment
*align
= &table
->alignment
;
68 int i
, ret
, dfll_mv
, min_mv
, max_mv
;
70 min_mv
= round_voltage(table
->min_millivolts
, align
, UP
);
71 max_mv
= round_voltage(table
->max_millivolts
, align
, DOWN
);
73 for (i
= 0; i
< MAX_DVFS_FREQS
; i
++) {
74 const struct cvb_table_freq_entry
*entry
= &table
->entries
[i
];
76 if (!entry
->freq
|| (entry
->freq
> max_freq
))
79 dfll_mv
= get_cvb_voltage(speedo_value
, table
->speedo_scale
,
80 &entry
->coefficients
);
81 dfll_mv
= round_cvb_voltage(dfll_mv
, table
->voltage_scale
,
83 dfll_mv
= clamp(dfll_mv
, min_mv
, max_mv
);
85 ret
= dev_pm_opp_add(dev
, entry
->freq
, dfll_mv
* 1000);
94 * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables
95 * @dev: the struct device * for which the OPP table is built
96 * @tables: array of CVB tables
97 * @count: size of the previously mentioned array
98 * @process_id: process id of the HW module
99 * @speedo_id: speedo id of the HW module
100 * @speedo_value: speedo value of the HW module
101 * @max_freq: highest safe clock rate
103 * On Tegra, a CVB table encodes the relationship between operating voltage
104 * and safe maximal frequency for a given module (e.g. GPU or CPU). This
105 * function calculates the optimal voltage-frequency operating points
106 * for the given arguments and exports them via the OPP library for the
107 * given @dev. Returns a pointer to the struct cvb_table that matched
108 * or an ERR_PTR on failure.
110 const struct cvb_table
*
111 tegra_cvb_add_opp_table(struct device
*dev
, const struct cvb_table
*tables
,
112 size_t count
, int process_id
, int speedo_id
,
113 int speedo_value
, unsigned long max_freq
)
118 for (i
= 0; i
< count
; i
++) {
119 const struct cvb_table
*table
= &tables
[i
];
121 if (table
->speedo_id
!= -1 && table
->speedo_id
!= speedo_id
)
124 if (table
->process_id
!= -1 && table
->process_id
!= process_id
)
127 ret
= build_opp_table(dev
, table
, speedo_value
, max_freq
);
128 return ret
? ERR_PTR(ret
) : table
;
131 return ERR_PTR(-EINVAL
);
134 void tegra_cvb_remove_opp_table(struct device
*dev
,
135 const struct cvb_table
*table
,
136 unsigned long max_freq
)
140 for (i
= 0; i
< MAX_DVFS_FREQS
; i
++) {
141 const struct cvb_table_freq_entry
*entry
= &table
->entries
[i
];
143 if (!entry
->freq
|| (entry
->freq
> max_freq
))
146 dev_pm_opp_remove(dev
, entry
->freq
);