Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / gpu / drm / nouveau / nvkm / subdev / volt / base.c
1 /*
2  * Copyright 2013 Red Hat Inc.
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 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.
21  *
22  * Authors: Ben Skeggs
23  */
24 #include <subdev/volt.h>
25 #include <subdev/bios.h>
26 #include <subdev/bios/vmap.h>
27 #include <subdev/bios/volt.h>
28
29 static int
30 nvkm_volt_get(struct nvkm_volt *volt)
31 {
32         if (volt->vid_get) {
33                 int ret = volt->vid_get(volt), i;
34                 if (ret >= 0) {
35                         for (i = 0; i < volt->vid_nr; i++) {
36                                 if (volt->vid[i].vid == ret)
37                                         return volt->vid[i].uv;
38                         }
39                         ret = -EINVAL;
40                 }
41                 return ret;
42         }
43         return -ENODEV;
44 }
45
46 static int
47 nvkm_volt_set(struct nvkm_volt *volt, u32 uv)
48 {
49         if (volt->vid_set) {
50                 int i, ret = -EINVAL;
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);
55                                 break;
56                         }
57                 }
58                 return ret;
59         }
60         return -ENODEV;
61 }
62
63 static int
64 nvkm_volt_map(struct nvkm_volt *volt, u8 id)
65 {
66         struct nvkm_bios *bios = nvkm_bios(volt);
67         struct nvbios_vmap_entry info;
68         u8  ver, len;
69         u16 vmap;
70
71         vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
72         if (vmap) {
73                 if (info.link != 0xff) {
74                         int ret = nvkm_volt_map(volt, info.link);
75                         if (ret < 0)
76                                 return ret;
77                         info.min += ret;
78                 }
79                 return info.min;
80         }
81
82         return id ? id * 10000 : -ENODEV;
83 }
84
85 static int
86 nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition)
87 {
88         int ret = nvkm_volt_map(volt, id);
89         if (ret >= 0) {
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);
95                 } else {
96                         ret = 0;
97                 }
98         }
99         return ret;
100 }
101
102 static void
103 nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
104 {
105         struct nvbios_volt_entry ivid;
106         struct nvbios_volt info;
107         u8  ver, hdr, cnt, len;
108         u16 data;
109         int i;
110
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;
118                                 volt->vid_nr++;
119                         }
120                         info.base += info.step;
121                 }
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,
126                                                        &ivid);
127                         if (data) {
128                                 volt->vid[volt->vid_nr].uv = ivid.voltage;
129                                 volt->vid[volt->vid_nr].vid = ivid.vid;
130                                 volt->vid_nr++;
131                         }
132                 }
133                 volt->vid_mask = info.vidmask;
134         }
135 }
136
137 int
138 _nvkm_volt_init(struct nvkm_object *object)
139 {
140         struct nvkm_volt *volt = (void *)object;
141         int ret;
142
143         ret = nvkm_subdev_init(&volt->base);
144         if (ret)
145                 return ret;
146
147         ret = volt->get(volt);
148         if (ret < 0) {
149                 if (ret != -ENODEV)
150                         nv_debug(volt, "current voltage unknown\n");
151                 return 0;
152         }
153
154         nv_info(volt, "GPU voltage: %duv\n", ret);
155         return 0;
156 }
157
158 void
159 _nvkm_volt_dtor(struct nvkm_object *object)
160 {
161         struct nvkm_volt *volt = (void *)object;
162         nvkm_subdev_destroy(&volt->base);
163 }
164
165 int
166 nvkm_volt_create_(struct nvkm_object *parent, struct nvkm_object *engine,
167                   struct nvkm_oclass *oclass, int length, void **pobject)
168 {
169         struct nvkm_bios *bios = nvkm_bios(parent);
170         struct nvkm_volt *volt;
171         int ret, i;
172
173         ret = nvkm_subdev_create_(parent, engine, oclass, 0, "VOLT",
174                                   "voltage", length, pobject);
175         volt = *pobject;
176         if (ret)
177                 return ret;
178
179         volt->get = nvkm_volt_get;
180         volt->set = nvkm_volt_set;
181         volt->set_id = nvkm_volt_set_id;
182
183         /* Assuming the non-bios device should build the voltage table later */
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                         nv_debug(volt, "VID %02x: %duv\n",
190                                  volt->vid[i].vid, volt->vid[i].uv);
191                 }
192
193                 /*XXX: this is an assumption.. there probably exists boards
194                  * out there with i2c-connected voltage controllers too..
195                  */
196                 ret = nvkm_voltgpio_init(volt);
197                 if (ret == 0) {
198                         volt->vid_get = nvkm_voltgpio_get;
199                         volt->vid_set = nvkm_voltgpio_set;
200                 }
201         }
202
203         return ret;
204 }