2 * Copyright 2013 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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 COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
26 #include <subdev/bios.h>
27 #include <subdev/bios/vmap.h>
28 #include <subdev/bios/volt.h>
29 #include <subdev/therm.h>
32 nvkm_volt_get(struct nvkm_volt
*volt
)
36 if (volt
->func
->volt_get
)
37 return volt
->func
->volt_get(volt
);
39 ret
= volt
->func
->vid_get(volt
);
41 for (i
= 0; i
< volt
->vid_nr
; i
++) {
42 if (volt
->vid
[i
].vid
== ret
)
43 return volt
->vid
[i
].uv
;
51 nvkm_volt_set(struct nvkm_volt
*volt
, u32 uv
)
53 struct nvkm_subdev
*subdev
= &volt
->subdev
;
54 int i
, ret
= -EINVAL
, best_err
= volt
->max_uv
, best
= -1;
56 if (volt
->func
->volt_set
)
57 return volt
->func
->volt_set(volt
, uv
);
59 for (i
= 0; i
< volt
->vid_nr
; i
++) {
60 int err
= volt
->vid
[i
].uv
- uv
;
61 if (err
< 0 || err
> best_err
)
71 nvkm_error(subdev
, "couldn't set %iuv\n", uv
);
75 ret
= volt
->func
->vid_set(volt
, volt
->vid
[best
].vid
);
76 nvkm_debug(subdev
, "set req %duv to %duv: %d\n", uv
,
77 volt
->vid
[best
].uv
, ret
);
82 nvkm_volt_map_min(struct nvkm_volt
*volt
, u8 id
)
84 struct nvkm_bios
*bios
= volt
->subdev
.device
->bios
;
85 struct nvbios_vmap_entry info
;
89 vmap
= nvbios_vmap_entry_parse(bios
, id
, &ver
, &len
, &info
);
91 if (info
.link
!= 0xff) {
92 int ret
= nvkm_volt_map_min(volt
, info
.link
);
100 return id
? id
* 10000 : -ENODEV
;
104 nvkm_volt_map(struct nvkm_volt
*volt
, u8 id
, u8 temp
)
106 struct nvkm_bios
*bios
= volt
->subdev
.device
->bios
;
107 struct nvbios_vmap_entry info
;
111 vmap
= nvbios_vmap_entry_parse(bios
, id
, &ver
, &len
, &info
);
115 if (volt
->speedo
< 0)
118 if (ver
== 0x10 || (ver
== 0x20 && info
.mode
== 0)) {
119 result
= div64_s64((s64
)info
.arg
[0], 10);
120 result
+= div64_s64((s64
)info
.arg
[1] * volt
->speedo
, 10);
121 result
+= div64_s64((s64
)info
.arg
[2] * volt
->speedo
* volt
->speedo
, 100000);
122 } else if (ver
== 0x20) {
124 /* 0x0 handled above! */
126 result
= ((s64
)info
.arg
[0] * 15625) >> 18;
127 result
+= ((s64
)info
.arg
[1] * volt
->speedo
* 15625) >> 18;
128 result
+= ((s64
)info
.arg
[2] * temp
* 15625) >> 10;
129 result
+= ((s64
)info
.arg
[3] * volt
->speedo
* temp
* 15625) >> 18;
130 result
+= ((s64
)info
.arg
[4] * volt
->speedo
* volt
->speedo
* 15625) >> 30;
131 result
+= ((s64
)info
.arg
[5] * temp
* temp
* 15625) >> 18;
134 result
= (info
.min
+ info
.max
) / 2;
145 result
= min(max(result
, (s64
)info
.min
), (s64
)info
.max
);
147 if (info
.link
!= 0xff) {
148 int ret
= nvkm_volt_map(volt
, info
.link
, temp
);
156 return id
? id
* 10000 : -ENODEV
;
160 nvkm_volt_set_id(struct nvkm_volt
*volt
, u8 id
, u8 min_id
, u8 temp
,
165 if (volt
->func
->set_id
)
166 return volt
->func
->set_id(volt
, id
, condition
);
168 ret
= nvkm_volt_map(volt
, id
, temp
);
170 int prev
= nvkm_volt_get(volt
);
171 if (!condition
|| prev
< 0 ||
172 (condition
< 0 && ret
< prev
) ||
173 (condition
> 0 && ret
> prev
)) {
174 int min
= nvkm_volt_map(volt
, min_id
, temp
);
177 ret
= nvkm_volt_set(volt
, ret
);
186 nvkm_volt_parse_bios(struct nvkm_bios
*bios
, struct nvkm_volt
*volt
)
188 struct nvkm_subdev
*subdev
= &bios
->subdev
;
189 struct nvbios_volt_entry ivid
;
190 struct nvbios_volt info
;
191 u8 ver
, hdr
, cnt
, len
;
195 data
= nvbios_volt_parse(bios
, &ver
, &hdr
, &cnt
, &len
, &info
);
196 if (data
&& info
.vidmask
&& info
.base
&& info
.step
&& info
.ranged
) {
197 nvkm_debug(subdev
, "found ranged based VIDs\n");
198 volt
->min_uv
= info
.min
;
199 volt
->max_uv
= info
.max
;
200 for (i
= 0; i
< info
.vidmask
+ 1; i
++) {
201 if (info
.base
>= info
.min
&&
202 info
.base
<= info
.max
) {
203 volt
->vid
[volt
->vid_nr
].uv
= info
.base
;
204 volt
->vid
[volt
->vid_nr
].vid
= i
;
207 info
.base
+= info
.step
;
209 volt
->vid_mask
= info
.vidmask
;
210 } else if (data
&& info
.vidmask
&& !info
.ranged
) {
211 nvkm_debug(subdev
, "found entry based VIDs\n");
212 volt
->min_uv
= 0xffffffff;
214 for (i
= 0; i
< cnt
; i
++) {
215 data
= nvbios_volt_entry_parse(bios
, i
, &ver
, &hdr
,
218 volt
->vid
[volt
->vid_nr
].uv
= ivid
.voltage
;
219 volt
->vid
[volt
->vid_nr
].vid
= ivid
.vid
;
221 volt
->min_uv
= min(volt
->min_uv
, ivid
.voltage
);
222 volt
->max_uv
= max(volt
->max_uv
, ivid
.voltage
);
225 volt
->vid_mask
= info
.vidmask
;
226 } else if (data
&& info
.type
== NVBIOS_VOLT_PWM
) {
227 volt
->min_uv
= info
.base
;
228 volt
->max_uv
= info
.base
+ info
.pwm_range
;
233 nvkm_volt_speedo_read(struct nvkm_volt
*volt
)
235 if (volt
->func
->speedo_read
)
236 return volt
->func
->speedo_read(volt
);
241 nvkm_volt_init(struct nvkm_subdev
*subdev
)
243 struct nvkm_volt
*volt
= nvkm_volt(subdev
);
244 int ret
= nvkm_volt_get(volt
);
247 nvkm_debug(subdev
, "current voltage unknown\n");
250 nvkm_debug(subdev
, "current voltage: %duv\n", ret
);
255 nvkm_volt_oneinit(struct nvkm_subdev
*subdev
)
257 struct nvkm_volt
*volt
= nvkm_volt(subdev
);
259 volt
->speedo
= nvkm_volt_speedo_read(volt
);
260 if (volt
->speedo
> 0)
261 nvkm_debug(&volt
->subdev
, "speedo %x\n", volt
->speedo
);
263 if (volt
->func
->oneinit
)
264 return volt
->func
->oneinit(volt
);
270 nvkm_volt_dtor(struct nvkm_subdev
*subdev
)
272 return nvkm_volt(subdev
);
275 static const struct nvkm_subdev_func
277 .dtor
= nvkm_volt_dtor
,
278 .init
= nvkm_volt_init
,
279 .oneinit
= nvkm_volt_oneinit
,
283 nvkm_volt_ctor(const struct nvkm_volt_func
*func
, struct nvkm_device
*device
,
284 int index
, struct nvkm_volt
*volt
)
286 struct nvkm_bios
*bios
= device
->bios
;
289 nvkm_subdev_ctor(&nvkm_volt
, device
, index
, &volt
->subdev
);
292 /* Assuming the non-bios device should build the voltage table later */
294 u8 ver
, hdr
, cnt
, len
;
295 struct nvbios_vmap vmap
;
297 nvkm_volt_parse_bios(bios
, volt
);
298 nvkm_debug(&volt
->subdev
, "min: %iuv max: %iuv\n",
299 volt
->min_uv
, volt
->max_uv
);
301 if (nvbios_vmap_parse(bios
, &ver
, &hdr
, &cnt
, &len
, &vmap
)) {
302 volt
->max0_id
= vmap
.max0
;
303 volt
->max1_id
= vmap
.max1
;
304 volt
->max2_id
= vmap
.max2
;
306 volt
->max0_id
= 0xff;
307 volt
->max1_id
= 0xff;
308 volt
->max2_id
= 0xff;
313 for (i
= 0; i
< volt
->vid_nr
; i
++) {
314 nvkm_debug(&volt
->subdev
, "VID %02x: %duv\n",
315 volt
->vid
[i
].vid
, volt
->vid
[i
].uv
);
321 nvkm_volt_new_(const struct nvkm_volt_func
*func
, struct nvkm_device
*device
,
322 int index
, struct nvkm_volt
**pvolt
)
324 if (!(*pvolt
= kzalloc(sizeof(**pvolt
), GFP_KERNEL
)))
326 nvkm_volt_ctor(func
, device
, index
, *pvolt
);