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