1 // SPDX-License-Identifier: GPL-2.0-only
3 * Utility functions for parsing Tegra CVB voltage tables
5 * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved.
8 #include <linux/kernel.h>
9 #include <linux/pm_opp.h>
13 /* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
14 static inline int get_cvb_voltage(int speedo
, int s_scale
,
15 const struct cvb_coefficients
*cvb
)
19 /* apply only speedo scale: output mv = cvb_mv * v_scale */
20 mv
= DIV_ROUND_CLOSEST(cvb
->c2
* speedo
, s_scale
);
21 mv
= DIV_ROUND_CLOSEST((mv
+ cvb
->c1
) * speedo
, s_scale
) + cvb
->c0
;
25 static int round_cvb_voltage(int mv
, int v_scale
,
26 const struct rail_alignment
*align
)
28 /* combined: apply voltage scale and round to cvb alignment step */
30 int step
= (align
->step_uv
? : 1000) * v_scale
;
31 int offset
= align
->offset_uv
* v_scale
;
33 uv
= max(mv
* 1000, offset
) - offset
;
34 uv
= DIV_ROUND_UP(uv
, step
) * align
->step_uv
+ align
->offset_uv
;
43 static int round_voltage(int mv
, const struct rail_alignment
*align
, int up
)
48 uv
= max(mv
* 1000, align
->offset_uv
) - align
->offset_uv
;
49 uv
= (uv
+ (up
? align
->step_uv
- 1 : 0)) / align
->step_uv
;
50 return (uv
* align
->step_uv
+ align
->offset_uv
) / 1000;
55 static int build_opp_table(struct device
*dev
, const struct cvb_table
*table
,
56 struct rail_alignment
*align
,
57 int speedo_value
, unsigned long max_freq
)
59 int i
, ret
, dfll_mv
, min_mv
, max_mv
;
61 min_mv
= round_voltage(table
->min_millivolts
, align
, UP
);
62 max_mv
= round_voltage(table
->max_millivolts
, align
, DOWN
);
64 for (i
= 0; i
< MAX_DVFS_FREQS
; i
++) {
65 const struct cvb_table_freq_entry
*entry
= &table
->entries
[i
];
67 if (!entry
->freq
|| (entry
->freq
> max_freq
))
70 dfll_mv
= get_cvb_voltage(speedo_value
, table
->speedo_scale
,
71 &entry
->coefficients
);
72 dfll_mv
= round_cvb_voltage(dfll_mv
, table
->voltage_scale
,
74 dfll_mv
= clamp(dfll_mv
, min_mv
, max_mv
);
76 ret
= dev_pm_opp_add(dev
, entry
->freq
, dfll_mv
* 1000);
85 * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables
86 * @dev: the struct device * for which the OPP table is built
87 * @tables: array of CVB tables
88 * @count: size of the previously mentioned array
89 * @process_id: process id of the HW module
90 * @speedo_id: speedo id of the HW module
91 * @speedo_value: speedo value of the HW module
92 * @max_freq: highest safe clock rate
94 * On Tegra, a CVB table encodes the relationship between operating voltage
95 * and safe maximal frequency for a given module (e.g. GPU or CPU). This
96 * function calculates the optimal voltage-frequency operating points
97 * for the given arguments and exports them via the OPP library for the
98 * given @dev. Returns a pointer to the struct cvb_table that matched
99 * or an ERR_PTR on failure.
101 const struct cvb_table
*
102 tegra_cvb_add_opp_table(struct device
*dev
, const struct cvb_table
*tables
,
103 size_t count
, struct rail_alignment
*align
,
104 int process_id
, int speedo_id
, int speedo_value
,
105 unsigned long max_freq
)
110 for (i
= 0; i
< count
; i
++) {
111 const struct cvb_table
*table
= &tables
[i
];
113 if (table
->speedo_id
!= -1 && table
->speedo_id
!= speedo_id
)
116 if (table
->process_id
!= -1 && table
->process_id
!= process_id
)
119 ret
= build_opp_table(dev
, table
, align
, speedo_value
,
121 return ret
? ERR_PTR(ret
) : table
;
124 return ERR_PTR(-EINVAL
);
127 void tegra_cvb_remove_opp_table(struct device
*dev
,
128 const struct cvb_table
*table
,
129 unsigned long max_freq
)
133 for (i
= 0; i
< MAX_DVFS_FREQS
; i
++) {
134 const struct cvb_table_freq_entry
*entry
= &table
->entries
[i
];
136 if (!entry
->freq
|| (entry
->freq
> max_freq
))
139 dev_pm_opp_remove(dev
, entry
->freq
);