GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / gpu / drm / nouveau / nvkm / subdev / bios / perf.c
1 /*
2  * Copyright 2012 Nouveau Community
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: Martin Peres
23  */
24 #include <subdev/bios.h>
25 #include <subdev/bios/bit.h>
26 #include <subdev/bios/perf.h>
27 #include <subdev/pci.h>
28
29 u32
30 nvbios_perf_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr,
31                   u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
32 {
33         struct bit_entry bit_P;
34         u32 perf = 0;
35
36         if (!bit_entry(bios, 'P', &bit_P)) {
37                 if (bit_P.version <= 2) {
38                         perf = nvbios_rd32(bios, bit_P.offset + 0);
39                         if (perf) {
40                                 *ver = nvbios_rd08(bios, perf + 0);
41                                 *hdr = nvbios_rd08(bios, perf + 1);
42                                 if (*ver >= 0x40 && *ver < 0x41) {
43                                         *cnt = nvbios_rd08(bios, perf + 5);
44                                         *len = nvbios_rd08(bios, perf + 2);
45                                         *snr = nvbios_rd08(bios, perf + 4);
46                                         *ssz = nvbios_rd08(bios, perf + 3);
47                                         return perf;
48                                 } else
49                                 if (*ver >= 0x20 && *ver < 0x40) {
50                                         *cnt = nvbios_rd08(bios, perf + 2);
51                                         *len = nvbios_rd08(bios, perf + 3);
52                                         *snr = nvbios_rd08(bios, perf + 4);
53                                         *ssz = nvbios_rd08(bios, perf + 5);
54                                         return perf;
55                                 }
56                         }
57                 }
58         }
59
60         if (bios->bmp_offset) {
61                 if (nvbios_rd08(bios, bios->bmp_offset + 6) >= 0x25) {
62                         perf = nvbios_rd16(bios, bios->bmp_offset + 0x94);
63                         if (perf) {
64                                 *hdr = nvbios_rd08(bios, perf + 0);
65                                 *ver = nvbios_rd08(bios, perf + 1);
66                                 *cnt = nvbios_rd08(bios, perf + 2);
67                                 *len = nvbios_rd08(bios, perf + 3);
68                                 *snr = 0;
69                                 *ssz = 0;
70                                 return perf;
71                         }
72                 }
73         }
74
75         return 0;
76 }
77
78 u32
79 nvbios_perf_entry(struct nvkm_bios *bios, int idx,
80                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
81 {
82         u8  snr, ssz;
83         u32 perf = nvbios_perf_table(bios, ver, hdr, cnt, len, &snr, &ssz);
84         if (perf && idx < *cnt) {
85                 perf = perf + *hdr + (idx * (*len + (snr * ssz)));
86                 *hdr = *len;
87                 *cnt = snr;
88                 *len = ssz;
89                 return perf;
90         }
91         return 0;
92 }
93
94 u32
95 nvbios_perfEp(struct nvkm_bios *bios, int idx,
96               u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info)
97 {
98         u32 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len);
99         memset(info, 0x00, sizeof(*info));
100         info->pstate = nvbios_rd08(bios, perf + 0x00);
101         switch (!!perf * *ver) {
102         case 0x12:
103         case 0x13:
104         case 0x14:
105                 info->core     = nvbios_rd32(bios, perf + 0x01) * 10;
106                 info->memory   = nvbios_rd32(bios, perf + 0x05) * 20;
107                 info->fanspeed = nvbios_rd08(bios, perf + 0x37);
108                 if (*hdr > 0x38)
109                         info->voltage = nvbios_rd08(bios, perf + 0x38);
110                 break;
111         case 0x21:
112         case 0x23:
113         case 0x24:
114                 info->fanspeed = nvbios_rd08(bios, perf + 0x04);
115                 info->voltage  = nvbios_rd08(bios, perf + 0x05);
116                 info->shader   = nvbios_rd16(bios, perf + 0x06) * 1000;
117                 info->core     = info->shader + (signed char)
118                                  nvbios_rd08(bios, perf + 0x08) * 1000;
119                 switch (bios->subdev.device->chipset) {
120                 case 0x49:
121                 case 0x4b:
122                         info->memory = nvbios_rd16(bios, perf + 0x0b) * 1000;
123                         break;
124                 default:
125                         info->memory = nvbios_rd16(bios, perf + 0x0b) * 2000;
126                         break;
127                 }
128                 break;
129         case 0x25:
130                 info->fanspeed = nvbios_rd08(bios, perf + 0x04);
131                 info->voltage  = nvbios_rd08(bios, perf + 0x05);
132                 info->core     = nvbios_rd16(bios, perf + 0x06) * 1000;
133                 info->shader   = nvbios_rd16(bios, perf + 0x0a) * 1000;
134                 info->memory   = nvbios_rd16(bios, perf + 0x0c) * 1000;
135                 break;
136         case 0x30:
137                 info->script   = nvbios_rd16(bios, perf + 0x02);
138         case 0x35:
139                 info->fanspeed = nvbios_rd08(bios, perf + 0x06);
140                 info->voltage  = nvbios_rd08(bios, perf + 0x07);
141                 info->core     = nvbios_rd16(bios, perf + 0x08) * 1000;
142                 info->shader   = nvbios_rd16(bios, perf + 0x0a) * 1000;
143                 info->memory   = nvbios_rd16(bios, perf + 0x0c) * 1000;
144                 info->vdec     = nvbios_rd16(bios, perf + 0x10) * 1000;
145                 info->disp     = nvbios_rd16(bios, perf + 0x14) * 1000;
146                 break;
147         case 0x40:
148                 info->voltage  = nvbios_rd08(bios, perf + 0x02);
149                 switch (nvbios_rd08(bios, perf + 0xb) & 0x3) {
150                 case 0:
151                         info->pcie_speed = NVKM_PCIE_SPEED_5_0;
152                         break;
153                 case 3:
154                 case 1:
155                         info->pcie_speed = NVKM_PCIE_SPEED_2_5;
156                         break;
157                 case 2:
158                         info->pcie_speed = NVKM_PCIE_SPEED_8_0;
159                         break;
160                 default:
161                         break;
162                 }
163                 info->pcie_width = 0xff;
164                 break;
165         default:
166                 return 0;
167         }
168         return perf;
169 }
170
171 u32
172 nvbios_perfSe(struct nvkm_bios *bios, u32 perfE, int idx,
173               u8 *ver, u8 *hdr, u8 cnt, u8 len)
174 {
175         u32 data = 0x00000000;
176         if (idx < cnt) {
177                 data = perfE + *hdr + (idx * len);
178                 *hdr = len;
179         }
180         return data;
181 }
182
183 u32
184 nvbios_perfSp(struct nvkm_bios *bios, u32 perfE, int idx,
185               u8 *ver, u8 *hdr, u8 cnt, u8 len,
186               struct nvbios_perfS *info)
187 {
188         u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len);
189         memset(info, 0x00, sizeof(*info));
190         switch (!!data * *ver) {
191         case 0x40:
192                 info->v40.freq = (nvbios_rd16(bios, data + 0x00) & 0x3fff) * 1000;
193                 break;
194         default:
195                 break;
196         }
197         return data;
198 }
199
200 int
201 nvbios_perf_fan_parse(struct nvkm_bios *bios,
202                       struct nvbios_perf_fan *fan)
203 {
204         u8  ver, hdr, cnt, len, snr, ssz;
205         u32 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
206         if (!perf)
207                 return -ENODEV;
208
209         if (ver >= 0x20 && ver < 0x40 && hdr > 6)
210                 fan->pwm_divisor = nvbios_rd16(bios, perf + 6);
211         else
212                 fan->pwm_divisor = 0;
213
214         return 0;
215 }