1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "priv.h"
25
26#include <subdev/bios.h>
27#include <subdev/bios/vmap.h>
28#include <subdev/bios/volt.h>
29
30int
31nvkm_volt_get(struct nvkm_volt *volt)
32{
33 int ret, i;
34
35 if (volt->func->volt_get)
36 return volt->func->volt_get(volt);
37
38 ret = volt->func->vid_get(volt);
39 if (ret >= 0) {
40 for (i = 0; i < volt->vid_nr; i++) {
41 if (volt->vid[i].vid == ret)
42 return volt->vid[i].uv;
43 }
44 ret = -EINVAL;
45 }
46 return ret;
47}
48
49static int
50nvkm_volt_set(struct nvkm_volt *volt, u32 uv)
51{
52 struct nvkm_subdev *subdev = &volt->subdev;
53 int i, ret = -EINVAL;
54
55 if (volt->func->volt_set)
56 return volt->func->volt_set(volt, uv);
57
58 for (i = 0; i < volt->vid_nr; i++) {
59 if (volt->vid[i].uv == uv) {
60 ret = volt->func->vid_set(volt, volt->vid[i].vid);
61 nvkm_debug(subdev, "set %duv: %d\n", uv, ret);
62 break;
63 }
64 }
65 return ret;
66}
67
68static int
69nvkm_volt_map(struct nvkm_volt *volt, u8 id)
70{
71 struct nvkm_bios *bios = volt->subdev.device->bios;
72 struct nvbios_vmap_entry info;
73 u8 ver, len;
74 u16 vmap;
75
76 vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
77 if (vmap) {
78 if (info.link != 0xff) {
79 int ret = nvkm_volt_map(volt, info.link);
80 if (ret < 0)
81 return ret;
82 info.min += ret;
83 }
84 return info.min;
85 }
86
87 return id ? id * 10000 : -ENODEV;
88}
89
90int
91nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition)
92{
93 int ret;
94
95 if (volt->func->set_id)
96 return volt->func->set_id(volt, id, condition);
97
98 ret = nvkm_volt_map(volt, id);
99 if (ret >= 0) {
100 int prev = nvkm_volt_get(volt);
101 if (!condition || prev < 0 ||
102 (condition < 0 && ret < prev) ||
103 (condition > 0 && ret > prev)) {
104 ret = nvkm_volt_set(volt, ret);
105 } else {
106 ret = 0;
107 }
108 }
109 return ret;
110}
111
112static void
113nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
114{
115 struct nvbios_volt_entry ivid;
116 struct nvbios_volt info;
117 u8 ver, hdr, cnt, len;
118 u16 data;
119 int i;
120
121 data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info);
122 if (data && info.vidmask && info.base && info.step) {
123 for (i = 0; i < info.vidmask + 1; i++) {
124 if (info.base >= info.min &&
125 info.base <= info.max) {
126 volt->vid[volt->vid_nr].uv = info.base;
127 volt->vid[volt->vid_nr].vid = i;
128 volt->vid_nr++;
129 }
130 info.base += info.step;
131 }
132 volt->vid_mask = info.vidmask;
133 } else if (data && info.vidmask) {
134 for (i = 0; i < cnt; i++) {
135 data = nvbios_volt_entry_parse(bios, i, &ver, &hdr,
136 &ivid);
137 if (data) {
138 volt->vid[volt->vid_nr].uv = ivid.voltage;
139 volt->vid[volt->vid_nr].vid = ivid.vid;
140 volt->vid_nr++;
141 }
142 }
143 volt->vid_mask = info.vidmask;
144 }
145}
146
147static int
148nvkm_volt_init(struct nvkm_subdev *subdev)
149{
150 struct nvkm_volt *volt = nvkm_volt(subdev);
151 int ret = nvkm_volt_get(volt);
152 if (ret < 0) {
153 if (ret != -ENODEV)
154 nvkm_debug(subdev, "current voltage unknown\n");
155 return 0;
156 }
157 nvkm_debug(subdev, "current voltage: %duv\n", ret);
158 return 0;
159}
160
161static void *
162nvkm_volt_dtor(struct nvkm_subdev *subdev)
163{
164 return nvkm_volt(subdev);
165}
166
167static const struct nvkm_subdev_func
168nvkm_volt = {
169 .dtor = nvkm_volt_dtor,
170 .init = nvkm_volt_init,
171};
172
173void
174nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device,
175 int index, struct nvkm_volt *volt)
176{
177 struct nvkm_bios *bios = device->bios;
178 int i;
179
180 nvkm_subdev_ctor(&nvkm_volt, device, index, &volt->subdev);
181 volt->func = func;
182
183
184 if (bios)
185 nvkm_volt_parse_bios(bios, volt);
186
187 if (volt->vid_nr) {
188 for (i = 0; i < volt->vid_nr; i++) {
189 nvkm_debug(&volt->subdev, "VID %02x: %duv\n",
190 volt->vid[i].vid, volt->vid[i].uv);
191 }
192 }
193}
194
195int
196nvkm_volt_new_(const struct nvkm_volt_func *func, struct nvkm_device *device,
197 int index, struct nvkm_volt **pvolt)
198{
199 if (!(*pvolt = kzalloc(sizeof(**pvolt), GFP_KERNEL)))
200 return -ENOMEM;
201 nvkm_volt_ctor(func, device, index, *pvolt);
202 return 0;
203}
204