1 /* $NetBSD: nouveau_nvkm_subdev_bios_therm.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $ */ 2 3 /* 4 * Copyright 2012 Nouveau Community 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Martin Peres 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_bios_therm.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $"); 28 29 #include <subdev/bios.h> 30 #include <subdev/bios/bit.h> 31 #include <subdev/bios/therm.h> 32 33 static u32 34 therm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) 35 { 36 struct bit_entry bit_P; 37 u32 therm = 0; 38 39 if (!bit_entry(bios, 'P', &bit_P)) { 40 if (bit_P.version == 1) 41 therm = nvbios_rd32(bios, bit_P.offset + 12); 42 else if (bit_P.version == 2) 43 therm = nvbios_rd32(bios, bit_P.offset + 16); 44 else 45 nvkm_error(&bios->subdev, 46 "unknown offset for thermal in BIT P %d\n", 47 bit_P.version); 48 } 49 50 /* exit now if we haven't found the thermal table */ 51 if (!therm) 52 return 0; 53 54 *ver = nvbios_rd08(bios, therm + 0); 55 *hdr = nvbios_rd08(bios, therm + 1); 56 *len = nvbios_rd08(bios, therm + 2); 57 *cnt = nvbios_rd08(bios, therm + 3); 58 return therm + nvbios_rd08(bios, therm + 1); 59 } 60 61 static u32 62 nvbios_therm_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) 63 { 64 u8 hdr, cnt; 65 u32 therm = therm_table(bios, ver, &hdr, len, &cnt); 66 if (therm && idx < cnt) 67 return therm + idx * *len; 68 return 0; 69 } 70 71 int 72 nvbios_therm_sensor_parse(struct nvkm_bios *bios, 73 enum nvbios_therm_domain domain, 74 struct nvbios_therm_sensor *sensor) 75 { 76 s8 thrs_section, sensor_section, offset; 77 u8 ver, len, i; 78 u32 entry; 79 80 /* we only support the core domain for now */ 81 if (domain != NVBIOS_THERM_DOMAIN_CORE) 82 return -EINVAL; 83 84 /* Read the entries from the table */ 85 thrs_section = 0; 86 sensor_section = -1; 87 i = 0; 88 while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { 89 s16 value = nvbios_rd16(bios, entry + 1); 90 91 switch (nvbios_rd08(bios, entry + 0)) { 92 case 0x0: 93 thrs_section = value; 94 if (value > 0) 95 return 0; /* we do not try to support ambient */ 96 break; 97 case 0x01: 98 sensor_section++; 99 if (sensor_section == 0) { 100 offset = ((s8) nvbios_rd08(bios, entry + 2)) / 2; 101 sensor->offset_constant = offset; 102 } 103 break; 104 105 case 0x04: 106 if (thrs_section == 0) { 107 sensor->thrs_critical.temp = (value & 0xff0) >> 4; 108 sensor->thrs_critical.hysteresis = value & 0xf; 109 } 110 break; 111 112 case 0x07: 113 if (thrs_section == 0) { 114 sensor->thrs_down_clock.temp = (value & 0xff0) >> 4; 115 sensor->thrs_down_clock.hysteresis = value & 0xf; 116 } 117 break; 118 119 case 0x08: 120 if (thrs_section == 0) { 121 sensor->thrs_fan_boost.temp = (value & 0xff0) >> 4; 122 sensor->thrs_fan_boost.hysteresis = value & 0xf; 123 } 124 break; 125 126 case 0x10: 127 if (sensor_section == 0) 128 sensor->offset_num = value; 129 break; 130 131 case 0x11: 132 if (sensor_section == 0) 133 sensor->offset_den = value; 134 break; 135 136 case 0x12: 137 if (sensor_section == 0) 138 sensor->slope_mult = value; 139 break; 140 141 case 0x13: 142 if (sensor_section == 0) 143 sensor->slope_div = value; 144 break; 145 case 0x32: 146 if (thrs_section == 0) { 147 sensor->thrs_shutdown.temp = (value & 0xff0) >> 4; 148 sensor->thrs_shutdown.hysteresis = value & 0xf; 149 } 150 break; 151 } 152 } 153 154 return 0; 155 } 156 157 int 158 nvbios_therm_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan) 159 { 160 struct nvbios_therm_trip_point *cur_trip = NULL; 161 u8 ver, len, i; 162 u32 entry; 163 164 uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0, 165 75, 0, 85, 0, 100, 0, 100, 0 }; 166 167 i = 0; 168 fan->nr_fan_trip = 0; 169 fan->fan_mode = NVBIOS_THERM_FAN_OTHER; 170 while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { 171 s16 value = nvbios_rd16(bios, entry + 1); 172 173 switch (nvbios_rd08(bios, entry + 0)) { 174 case 0x22: 175 fan->min_duty = value & 0xff; 176 fan->max_duty = (value & 0xff00) >> 8; 177 break; 178 case 0x24: 179 fan->nr_fan_trip++; 180 if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP) 181 fan->fan_mode = NVBIOS_THERM_FAN_TRIP; 182 cur_trip = &fan->trip[fan->nr_fan_trip - 1]; 183 cur_trip->hysteresis = value & 0xf; 184 cur_trip->temp = (value & 0xff0) >> 4; 185 cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12]; 186 break; 187 case 0x25: 188 cur_trip = &fan->trip[fan->nr_fan_trip - 1]; 189 cur_trip->fan_duty = value; 190 break; 191 case 0x26: 192 if (!fan->pwm_freq) 193 fan->pwm_freq = value; 194 break; 195 case 0x3b: 196 fan->bump_period = value; 197 break; 198 case 0x3c: 199 fan->slow_down_period = value; 200 break; 201 case 0x46: 202 if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR) 203 fan->fan_mode = NVBIOS_THERM_FAN_LINEAR; 204 fan->linear_min_temp = nvbios_rd08(bios, entry + 1); 205 fan->linear_max_temp = nvbios_rd08(bios, entry + 2); 206 break; 207 } 208 } 209 210 /* starting from fermi, fan management is always linear */ 211 if (bios->subdev.device->card_type >= NV_C0 && 212 fan->fan_mode == NVBIOS_THERM_FAN_OTHER) { 213 fan->fan_mode = NVBIOS_THERM_FAN_LINEAR; 214 } 215 216 return 0; 217 } 218