Commit | Line | Data |
---|---|---|
ef1df1bc | 1 | /* |
4158c9c2 | 2 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. |
ef1df1bc VH |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | * DEALINGS IN THE SOFTWARE. | |
21 | */ | |
437b2296 BS |
22 | #define gk20a_volt(p) container_of((p), struct gk20a_volt, base) |
23 | #include "priv.h" | |
24 | ||
43a70661 | 25 | #include <core/tegra.h> |
ef1df1bc | 26 | |
0f952093 | 27 | #include "gk20a.h" |
ef1df1bc | 28 | |
0f952093 | 29 | static const struct cvb_coef gk20a_cvb_coef[] = { |
ef1df1bc VH |
30 | /* MHz, c0, c1, c2, c3, c4, c5 */ |
31 | /* 72 */ { 1209886, -36468, 515, 417, -13123, 203}, | |
32 | /* 108 */ { 1130804, -27659, 296, 298, -10834, 221}, | |
33 | /* 180 */ { 1162871, -27110, 247, 238, -10681, 268}, | |
34 | /* 252 */ { 1220458, -28654, 247, 179, -10376, 298}, | |
35 | /* 324 */ { 1280953, -30204, 247, 119, -9766, 304}, | |
36 | /* 396 */ { 1344547, -31777, 247, 119, -8545, 292}, | |
37 | /* 468 */ { 1420168, -34227, 269, 60, -7172, 256}, | |
38 | /* 540 */ { 1490757, -35955, 274, 60, -5188, 197}, | |
39 | /* 612 */ { 1599112, -42583, 398, 0, -1831, 119}, | |
40 | /* 648 */ { 1366986, -16459, -274, 0, -3204, 72}, | |
41 | /* 684 */ { 1391884, -17078, -274, -60, -1526, 30}, | |
42 | /* 708 */ { 1415522, -17497, -274, -60, -458, 0}, | |
43 | /* 756 */ { 1464061, -18331, -274, -119, 1831, -72}, | |
44 | /* 804 */ { 1524225, -20064, -254, -119, 4272, -155}, | |
45 | /* 852 */ { 1608418, -21643, -269, 0, 763, -48}, | |
46 | }; | |
47 | ||
48 | /** | |
49 | * cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) | |
50 | */ | |
51 | static inline int | |
de3aaa66 | 52 | gk20a_volt_get_cvb_voltage(int speedo, int s_scale, const struct cvb_coef *coef) |
ef1df1bc VH |
53 | { |
54 | int mv; | |
55 | ||
56 | mv = DIV_ROUND_CLOSEST(coef->c2 * speedo, s_scale); | |
57 | mv = DIV_ROUND_CLOSEST((mv + coef->c1) * speedo, s_scale) + coef->c0; | |
58 | return mv; | |
59 | } | |
60 | ||
61 | /** | |
62 | * cvb_t_mv = | |
63 | * ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) + | |
64 | * ((c3 * speedo / s_scale + c4 + c5 * T / t_scale) * T / t_scale) | |
65 | */ | |
66 | static inline int | |
67 | gk20a_volt_get_cvb_t_voltage(int speedo, int temp, int s_scale, int t_scale, | |
de3aaa66 | 68 | const struct cvb_coef *coef) |
ef1df1bc VH |
69 | { |
70 | int cvb_mv, mv; | |
71 | ||
72 | cvb_mv = gk20a_volt_get_cvb_voltage(speedo, s_scale, coef); | |
73 | ||
74 | mv = DIV_ROUND_CLOSEST(coef->c3 * speedo, s_scale) + coef->c4 + | |
75 | DIV_ROUND_CLOSEST(coef->c5 * temp, t_scale); | |
76 | mv = DIV_ROUND_CLOSEST(mv * temp, t_scale) + cvb_mv; | |
77 | return mv; | |
78 | } | |
79 | ||
328bee46 | 80 | static int |
ef1df1bc VH |
81 | gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo) |
82 | { | |
ff318a51 | 83 | static const int v_scale = 1000; |
ef1df1bc VH |
84 | int mv; |
85 | ||
86 | mv = gk20a_volt_get_cvb_t_voltage(speedo, -10, 100, 10, coef); | |
ff318a51 | 87 | mv = DIV_ROUND_UP(mv, v_scale); |
ef1df1bc VH |
88 | |
89 | return mv * 1000; | |
90 | } | |
91 | ||
328bee46 | 92 | static int |
437b2296 | 93 | gk20a_volt_vid_get(struct nvkm_volt *base) |
ef1df1bc | 94 | { |
437b2296 | 95 | struct gk20a_volt *volt = gk20a_volt(base); |
ef1df1bc VH |
96 | int i, uv; |
97 | ||
6052dc57 | 98 | uv = regulator_get_voltage(volt->vdd); |
ef1df1bc | 99 | |
6052dc57 BS |
100 | for (i = 0; i < volt->base.vid_nr; i++) |
101 | if (volt->base.vid[i].uv >= uv) | |
ef1df1bc VH |
102 | return i; |
103 | ||
104 | return -EINVAL; | |
105 | } | |
106 | ||
328bee46 | 107 | static int |
437b2296 | 108 | gk20a_volt_vid_set(struct nvkm_volt *base, u8 vid) |
ef1df1bc | 109 | { |
437b2296 | 110 | struct gk20a_volt *volt = gk20a_volt(base); |
185eda33 | 111 | struct nvkm_subdev *subdev = &volt->base.subdev; |
ef1df1bc | 112 | |
185eda33 | 113 | nvkm_debug(subdev, "set voltage as %duv\n", volt->base.vid[vid].uv); |
6052dc57 | 114 | return regulator_set_voltage(volt->vdd, volt->base.vid[vid].uv, 1200000); |
ef1df1bc VH |
115 | } |
116 | ||
328bee46 | 117 | static int |
437b2296 | 118 | gk20a_volt_set_id(struct nvkm_volt *base, u8 id, int condition) |
ef1df1bc | 119 | { |
437b2296 | 120 | struct gk20a_volt *volt = gk20a_volt(base); |
185eda33 | 121 | struct nvkm_subdev *subdev = &volt->base.subdev; |
6052dc57 BS |
122 | int prev_uv = regulator_get_voltage(volt->vdd); |
123 | int target_uv = volt->base.vid[id].uv; | |
ef1df1bc VH |
124 | int ret; |
125 | ||
185eda33 BS |
126 | nvkm_debug(subdev, "prev=%d, target=%d, condition=%d\n", |
127 | prev_uv, target_uv, condition); | |
ef1df1bc VH |
128 | if (!condition || |
129 | (condition < 0 && target_uv < prev_uv) || | |
130 | (condition > 0 && target_uv > prev_uv)) { | |
6052dc57 | 131 | ret = gk20a_volt_vid_set(&volt->base, volt->base.vid[id].vid); |
ef1df1bc VH |
132 | } else { |
133 | ret = 0; | |
134 | } | |
135 | ||
136 | return ret; | |
137 | } | |
138 | ||
437b2296 BS |
139 | static const struct nvkm_volt_func |
140 | gk20a_volt = { | |
141 | .vid_get = gk20a_volt_vid_get, | |
142 | .vid_set = gk20a_volt_vid_set, | |
143 | .set_id = gk20a_volt_set_id, | |
144 | }; | |
145 | ||
146 | int | |
ebe5e526 AC |
147 | gk20a_volt_ctor(struct nvkm_device *device, int index, |
148 | const struct cvb_coef *coefs, int nb_coefs, | |
791eeea2 | 149 | int vmin, struct gk20a_volt *volt) |
ef1df1bc | 150 | { |
43a70661 | 151 | struct nvkm_device_tegra *tdev = device->func->tegra(device); |
437b2296 BS |
152 | int i, uv; |
153 | ||
437b2296 | 154 | nvkm_volt_ctor(&gk20a_volt, device, index, &volt->base); |
ef1df1bc | 155 | |
43a70661 | 156 | uv = regulator_get_voltage(tdev->vdd); |
4158c9c2 | 157 | nvkm_debug(&volt->base.subdev, "the default voltage is %duV\n", uv); |
6052dc57 | 158 | |
43a70661 | 159 | volt->vdd = tdev->vdd; |
6052dc57 | 160 | |
4158c9c2 | 161 | volt->base.vid_nr = nb_coefs; |
6052dc57 BS |
162 | for (i = 0; i < volt->base.vid_nr; i++) { |
163 | volt->base.vid[i].vid = i; | |
791eeea2 AC |
164 | volt->base.vid[i].uv = max( |
165 | gk20a_volt_calc_voltage(&coefs[i], tdev->gpu_speedo), | |
166 | vmin); | |
185eda33 BS |
167 | nvkm_debug(&volt->base.subdev, "%2d: vid=%d, uv=%d\n", i, |
168 | volt->base.vid[i].vid, volt->base.vid[i].uv); | |
ef1df1bc VH |
169 | } |
170 | ||
171 | return 0; | |
172 | } | |
4158c9c2 AC |
173 | |
174 | int | |
175 | gk20a_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) | |
176 | { | |
177 | struct gk20a_volt *volt; | |
178 | ||
179 | volt = kzalloc(sizeof(*volt), GFP_KERNEL); | |
180 | if (!volt) | |
181 | return -ENOMEM; | |
182 | *pvolt = &volt->base; | |
183 | ||
ebe5e526 | 184 | return gk20a_volt_ctor(device, index, gk20a_cvb_coef, |
791eeea2 | 185 | ARRAY_SIZE(gk20a_cvb_coef), 0, volt); |
4158c9c2 | 186 | } |