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.
24 #include <subdev/volt.h>
25 #include <subdev/bios.h>
26 #include <subdev/bios/vmap.h>
27 #include <subdev/bios/volt.h>
30 nvkm_volt_get(struct nvkm_volt
*volt
)
33 int ret
= volt
->vid_get(volt
), i
;
35 for (i
= 0; i
< volt
->vid_nr
; i
++) {
36 if (volt
->vid
[i
].vid
== ret
)
37 return volt
->vid
[i
].uv
;
47 nvkm_volt_set(struct nvkm_volt
*volt
, u32 uv
)
51 for (i
= 0; i
< volt
->vid_nr
; i
++) {
52 if (volt
->vid
[i
].uv
== uv
) {
53 ret
= volt
->vid_set(volt
, volt
->vid
[i
].vid
);
54 nv_debug(volt
, "set %duv: %d\n", uv
, ret
);
64 nvkm_volt_map(struct nvkm_volt
*volt
, u8 id
)
66 struct nvkm_bios
*bios
= nvkm_bios(volt
);
67 struct nvbios_vmap_entry info
;
71 vmap
= nvbios_vmap_entry_parse(bios
, id
, &ver
, &len
, &info
);
73 if (info
.link
!= 0xff) {
74 int ret
= nvkm_volt_map(volt
, info
.link
);
82 return id
? id
* 10000 : -ENODEV
;
86 nvkm_volt_set_id(struct nvkm_volt
*volt
, u8 id
, int condition
)
88 int ret
= nvkm_volt_map(volt
, id
);
90 int prev
= nvkm_volt_get(volt
);
91 if (!condition
|| prev
< 0 ||
92 (condition
< 0 && ret
< prev
) ||
93 (condition
> 0 && ret
> prev
)) {
94 ret
= nvkm_volt_set(volt
, ret
);
103 nvkm_volt_parse_bios(struct nvkm_bios
*bios
, struct nvkm_volt
*volt
)
105 struct nvbios_volt_entry ivid
;
106 struct nvbios_volt info
;
107 u8 ver
, hdr
, cnt
, len
;
111 data
= nvbios_volt_parse(bios
, &ver
, &hdr
, &cnt
, &len
, &info
);
112 if (data
&& info
.vidmask
&& info
.base
&& info
.step
) {
113 for (i
= 0; i
< info
.vidmask
+ 1; i
++) {
114 if (info
.base
>= info
.min
&&
115 info
.base
<= info
.max
) {
116 volt
->vid
[volt
->vid_nr
].uv
= info
.base
;
117 volt
->vid
[volt
->vid_nr
].vid
= i
;
120 info
.base
+= info
.step
;
122 volt
->vid_mask
= info
.vidmask
;
123 } else if (data
&& info
.vidmask
) {
124 for (i
= 0; i
< cnt
; i
++) {
125 data
= nvbios_volt_entry_parse(bios
, i
, &ver
, &hdr
,
128 volt
->vid
[volt
->vid_nr
].uv
= ivid
.voltage
;
129 volt
->vid
[volt
->vid_nr
].vid
= ivid
.vid
;
133 volt
->vid_mask
= info
.vidmask
;
138 _nvkm_volt_init(struct nvkm_object
*object
)
140 struct nvkm_volt
*volt
= (void *)object
;
143 ret
= nvkm_subdev_init(&volt
->base
);
147 ret
= volt
->get(volt
);
150 nv_debug(volt
, "current voltage unknown\n");
154 nv_info(volt
, "GPU voltage: %duv\n", ret
);
159 _nvkm_volt_dtor(struct nvkm_object
*object
)
161 struct nvkm_volt
*volt
= (void *)object
;
162 nvkm_subdev_destroy(&volt
->base
);
166 nvkm_volt_create_(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
167 struct nvkm_oclass
*oclass
, int length
, void **pobject
)
169 struct nvkm_bios
*bios
= nvkm_bios(parent
);
170 struct nvkm_volt
*volt
;
173 ret
= nvkm_subdev_create_(parent
, engine
, oclass
, 0, "VOLT",
174 "voltage", length
, pobject
);
179 volt
->get
= nvkm_volt_get
;
180 volt
->set
= nvkm_volt_set
;
181 volt
->set_id
= nvkm_volt_set_id
;
183 /* Assuming the non-bios device should build the voltage table later */
185 nvkm_volt_parse_bios(bios
, volt
);
188 for (i
= 0; i
< volt
->vid_nr
; i
++) {
189 nv_debug(volt
, "VID %02x: %duv\n",
190 volt
->vid
[i
].vid
, volt
->vid
[i
].uv
);
193 /*XXX: this is an assumption.. there probably exists boards
194 * out there with i2c-connected voltage controllers too..
196 ret
= nvkm_voltgpio_init(volt
);
198 volt
->vid_get
= nvkm_voltgpio_get
;
199 volt
->vid_set
= nvkm_voltgpio_set
;