1 /* $NetBSD: nouveau_nvkm_subdev_therm_base.c,v 1.4 2021/12/19 11:34:46 riastradh Exp $ */ 2 3 /* 4 * Copyright 2012 The 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_therm_base.c,v 1.4 2021/12/19 11:34:46 riastradh Exp $"); 28 29 #include "priv.h" 30 31 #include <core/option.h> 32 #include <subdev/pmu.h> 33 34 int 35 nvkm_therm_temp_get(struct nvkm_therm *therm) 36 { 37 if (therm->func->temp_get) 38 return therm->func->temp_get(therm); 39 return -ENODEV; 40 } 41 42 static int 43 nvkm_therm_update_trip(struct nvkm_therm *therm) 44 { 45 struct nvbios_therm_trip_point *trip = therm->fan->bios.trip, 46 *cur_trip = NULL, 47 *last_trip = therm->last_trip; 48 u8 temp = therm->func->temp_get(therm); 49 u16 duty, i; 50 51 /* look for the trip point corresponding to the current temperature */ 52 cur_trip = NULL; 53 for (i = 0; i < therm->fan->bios.nr_fan_trip; i++) { 54 if (temp >= trip[i].temp) 55 cur_trip = &trip[i]; 56 } 57 58 /* account for the hysteresis cycle */ 59 if (last_trip && temp <= (last_trip->temp) && 60 temp > (last_trip->temp - last_trip->hysteresis)) 61 cur_trip = last_trip; 62 63 if (cur_trip) { 64 duty = cur_trip->fan_duty; 65 therm->last_trip = cur_trip; 66 } else { 67 duty = 0; 68 therm->last_trip = NULL; 69 } 70 71 return duty; 72 } 73 74 static int 75 nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8 linear_min_temp, 76 u8 linear_max_temp) 77 { 78 u8 temp = therm->func->temp_get(therm); 79 u16 duty; 80 81 /* handle the non-linear part first */ 82 if (temp < linear_min_temp) 83 return therm->fan->bios.min_duty; 84 else if (temp > linear_max_temp) 85 return therm->fan->bios.max_duty; 86 87 /* we are in the linear zone */ 88 duty = (temp - linear_min_temp); 89 duty *= (therm->fan->bios.max_duty - therm->fan->bios.min_duty); 90 duty /= (linear_max_temp - linear_min_temp); 91 duty += therm->fan->bios.min_duty; 92 return duty; 93 } 94 95 static int 96 nvkm_therm_update_linear(struct nvkm_therm *therm) 97 { 98 u8 min = therm->fan->bios.linear_min_temp; 99 u8 max = therm->fan->bios.linear_max_temp; 100 return nvkm_therm_compute_linear_duty(therm, min, max); 101 } 102 103 static int 104 nvkm_therm_update_linear_fallback(struct nvkm_therm *therm) 105 { 106 u8 max = therm->bios_sensor.thrs_fan_boost.temp; 107 return nvkm_therm_compute_linear_duty(therm, 30, max); 108 } 109 110 static void 111 nvkm_therm_update(struct nvkm_therm *therm, int mode) 112 { 113 struct nvkm_subdev *subdev = &therm->subdev; 114 struct nvkm_timer *tmr = subdev->device->timer; 115 unsigned long flags; 116 bool immd = true; 117 bool poll = true; 118 int duty = -1; 119 120 spin_lock_irqsave(&therm->lock, flags); 121 if (mode < 0) 122 mode = therm->mode; 123 therm->mode = mode; 124 125 switch (mode) { 126 case NVKM_THERM_CTRL_MANUAL: 127 nvkm_timer_alarm(tmr, 0, &therm->alarm); 128 duty = nvkm_therm_fan_get(therm); 129 if (duty < 0) 130 duty = 100; 131 poll = false; 132 break; 133 case NVKM_THERM_CTRL_AUTO: 134 switch(therm->fan->bios.fan_mode) { 135 case NVBIOS_THERM_FAN_TRIP: 136 duty = nvkm_therm_update_trip(therm); 137 break; 138 case NVBIOS_THERM_FAN_LINEAR: 139 duty = nvkm_therm_update_linear(therm); 140 break; 141 case NVBIOS_THERM_FAN_OTHER: 142 if (therm->cstate) { 143 duty = therm->cstate; 144 poll = false; 145 } else { 146 duty = nvkm_therm_update_linear_fallback(therm); 147 } 148 break; 149 } 150 immd = false; 151 break; 152 case NVKM_THERM_CTRL_NONE: 153 default: 154 nvkm_timer_alarm(tmr, 0, &therm->alarm); 155 poll = false; 156 } 157 158 if (poll) 159 nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm); 160 spin_unlock_irqrestore(&therm->lock, flags); 161 162 if (duty >= 0) { 163 #if 0 /* XXXMRG one log per second is a little excessive */ 164 nvkm_debug(subdev, "FAN target request: %d%%\n", duty); 165 #endif 166 nvkm_therm_fan_set(therm, immd, duty); 167 } 168 } 169 170 int 171 nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int dir) 172 { 173 struct nvkm_subdev *subdev = &therm->subdev; 174 if (!dir || (dir < 0 && fan < therm->cstate) || 175 (dir > 0 && fan > therm->cstate)) { 176 nvkm_debug(subdev, "default fan speed -> %d%%\n", fan); 177 therm->cstate = fan; 178 nvkm_therm_update(therm, -1); 179 } 180 return 0; 181 } 182 183 static void 184 nvkm_therm_alarm(struct nvkm_alarm *alarm) 185 { 186 struct nvkm_therm *therm = 187 container_of(alarm, struct nvkm_therm, alarm); 188 nvkm_therm_update(therm, -1); 189 } 190 191 int 192 nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode) 193 { 194 struct nvkm_subdev *subdev = &therm->subdev; 195 struct nvkm_device *device = subdev->device; 196 static const char *name[] = { 197 "disabled", 198 "manual", 199 "automatic" 200 }; 201 202 /* The default PPWR ucode on fermi interferes with fan management */ 203 if ((mode >= ARRAY_SIZE(name)) || 204 (mode != NVKM_THERM_CTRL_NONE && nvkm_pmu_fan_controlled(device))) 205 return -EINVAL; 206 207 /* do not allow automatic fan management if the thermal sensor is 208 * not available */ 209 if (mode == NVKM_THERM_CTRL_AUTO && 210 therm->func->temp_get(therm) < 0) 211 return -EINVAL; 212 213 if (therm->mode == mode) 214 return 0; 215 216 nvkm_debug(subdev, "fan management: %s\n", name[mode]); 217 nvkm_therm_update(therm, mode); 218 return 0; 219 } 220 221 int 222 nvkm_therm_attr_get(struct nvkm_therm *therm, enum nvkm_therm_attr_type type) 223 { 224 switch (type) { 225 case NVKM_THERM_ATTR_FAN_MIN_DUTY: 226 return therm->fan->bios.min_duty; 227 case NVKM_THERM_ATTR_FAN_MAX_DUTY: 228 return therm->fan->bios.max_duty; 229 case NVKM_THERM_ATTR_FAN_MODE: 230 return therm->mode; 231 case NVKM_THERM_ATTR_THRS_FAN_BOOST: 232 return therm->bios_sensor.thrs_fan_boost.temp; 233 case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST: 234 return therm->bios_sensor.thrs_fan_boost.hysteresis; 235 case NVKM_THERM_ATTR_THRS_DOWN_CLK: 236 return therm->bios_sensor.thrs_down_clock.temp; 237 case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST: 238 return therm->bios_sensor.thrs_down_clock.hysteresis; 239 case NVKM_THERM_ATTR_THRS_CRITICAL: 240 return therm->bios_sensor.thrs_critical.temp; 241 case NVKM_THERM_ATTR_THRS_CRITICAL_HYST: 242 return therm->bios_sensor.thrs_critical.hysteresis; 243 case NVKM_THERM_ATTR_THRS_SHUTDOWN: 244 return therm->bios_sensor.thrs_shutdown.temp; 245 case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST: 246 return therm->bios_sensor.thrs_shutdown.hysteresis; 247 } 248 249 return -EINVAL; 250 } 251 252 int 253 nvkm_therm_attr_set(struct nvkm_therm *therm, 254 enum nvkm_therm_attr_type type, int value) 255 { 256 switch (type) { 257 case NVKM_THERM_ATTR_FAN_MIN_DUTY: 258 if (value < 0) 259 value = 0; 260 if (value > therm->fan->bios.max_duty) 261 value = therm->fan->bios.max_duty; 262 therm->fan->bios.min_duty = value; 263 return 0; 264 case NVKM_THERM_ATTR_FAN_MAX_DUTY: 265 if (value < 0) 266 value = 0; 267 if (value < therm->fan->bios.min_duty) 268 value = therm->fan->bios.min_duty; 269 therm->fan->bios.max_duty = value; 270 return 0; 271 case NVKM_THERM_ATTR_FAN_MODE: 272 return nvkm_therm_fan_mode(therm, value); 273 case NVKM_THERM_ATTR_THRS_FAN_BOOST: 274 therm->bios_sensor.thrs_fan_boost.temp = value; 275 therm->func->program_alarms(therm); 276 return 0; 277 case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST: 278 therm->bios_sensor.thrs_fan_boost.hysteresis = value; 279 therm->func->program_alarms(therm); 280 return 0; 281 case NVKM_THERM_ATTR_THRS_DOWN_CLK: 282 therm->bios_sensor.thrs_down_clock.temp = value; 283 therm->func->program_alarms(therm); 284 return 0; 285 case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST: 286 therm->bios_sensor.thrs_down_clock.hysteresis = value; 287 therm->func->program_alarms(therm); 288 return 0; 289 case NVKM_THERM_ATTR_THRS_CRITICAL: 290 therm->bios_sensor.thrs_critical.temp = value; 291 therm->func->program_alarms(therm); 292 return 0; 293 case NVKM_THERM_ATTR_THRS_CRITICAL_HYST: 294 therm->bios_sensor.thrs_critical.hysteresis = value; 295 therm->func->program_alarms(therm); 296 return 0; 297 case NVKM_THERM_ATTR_THRS_SHUTDOWN: 298 therm->bios_sensor.thrs_shutdown.temp = value; 299 therm->func->program_alarms(therm); 300 return 0; 301 case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST: 302 therm->bios_sensor.thrs_shutdown.hysteresis = value; 303 therm->func->program_alarms(therm); 304 return 0; 305 } 306 307 return -EINVAL; 308 } 309 310 void 311 nvkm_therm_clkgate_enable(struct nvkm_therm *therm) 312 { 313 if (!therm || !therm->func->clkgate_enable || !therm->clkgating_enabled) 314 return; 315 316 nvkm_debug(&therm->subdev, 317 "Enabling clockgating\n"); 318 therm->func->clkgate_enable(therm); 319 } 320 321 void 322 nvkm_therm_clkgate_fini(struct nvkm_therm *therm, bool suspend) 323 { 324 if (!therm || !therm->func->clkgate_fini || !therm->clkgating_enabled) 325 return; 326 327 nvkm_debug(&therm->subdev, 328 "Preparing clockgating for %s\n", 329 suspend ? "suspend" : "fini"); 330 therm->func->clkgate_fini(therm, suspend); 331 } 332 333 static void 334 nvkm_therm_clkgate_oneinit(struct nvkm_therm *therm) 335 { 336 if (!therm->func->clkgate_enable || !therm->clkgating_enabled) 337 return; 338 339 nvkm_info(&therm->subdev, "Clockgating enabled\n"); 340 } 341 342 static void 343 nvkm_therm_intr(struct nvkm_subdev *subdev) 344 { 345 struct nvkm_therm *therm = nvkm_therm(subdev); 346 if (therm->func->intr) 347 therm->func->intr(therm); 348 } 349 350 static int 351 nvkm_therm_fini(struct nvkm_subdev *subdev, bool suspend) 352 { 353 struct nvkm_therm *therm = nvkm_therm(subdev); 354 355 if (therm->func->fini) 356 therm->func->fini(therm); 357 358 nvkm_therm_fan_fini(therm, suspend); 359 nvkm_therm_sensor_fini(therm, suspend); 360 361 if (suspend) { 362 therm->suspend = therm->mode; 363 therm->mode = NVKM_THERM_CTRL_NONE; 364 } 365 366 return 0; 367 } 368 369 static int 370 nvkm_therm_oneinit(struct nvkm_subdev *subdev) 371 { 372 struct nvkm_therm *therm = nvkm_therm(subdev); 373 nvkm_therm_sensor_ctor(therm); 374 nvkm_therm_ic_ctor(therm); 375 nvkm_therm_fan_ctor(therm); 376 nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO); 377 nvkm_therm_sensor_preinit(therm); 378 nvkm_therm_clkgate_oneinit(therm); 379 return 0; 380 } 381 382 static int 383 nvkm_therm_init(struct nvkm_subdev *subdev) 384 { 385 struct nvkm_therm *therm = nvkm_therm(subdev); 386 387 if (therm->func->init) 388 therm->func->init(therm); 389 390 if (therm->suspend >= 0) { 391 /* restore the pwm value only when on manual or auto mode */ 392 if (therm->suspend > 0) 393 nvkm_therm_fan_set(therm, true, therm->fan->percent); 394 395 nvkm_therm_fan_mode(therm, therm->suspend); 396 } 397 398 nvkm_therm_sensor_init(therm); 399 nvkm_therm_fan_init(therm); 400 return 0; 401 } 402 403 void 404 nvkm_therm_clkgate_init(struct nvkm_therm *therm, 405 const struct nvkm_therm_clkgate_pack *p) 406 { 407 if (!therm || !therm->func->clkgate_init || !therm->clkgating_enabled) 408 return; 409 410 therm->func->clkgate_init(therm, p); 411 } 412 413 static void * 414 nvkm_therm_dtor(struct nvkm_subdev *subdev) 415 { 416 struct nvkm_therm *therm = nvkm_therm(subdev); 417 nvkm_therm_fan_dtor(therm); 418 kfree(therm->fan); 419 spin_lock_destroy(&therm->sensor.alarm_program_lock); 420 spin_lock_destroy(&therm->lock); 421 return therm; 422 } 423 424 static const struct nvkm_subdev_func 425 nvkm_therm = { 426 .dtor = nvkm_therm_dtor, 427 .oneinit = nvkm_therm_oneinit, 428 .init = nvkm_therm_init, 429 .fini = nvkm_therm_fini, 430 .intr = nvkm_therm_intr, 431 }; 432 433 void 434 nvkm_therm_ctor(struct nvkm_therm *therm, struct nvkm_device *device, 435 int index, const struct nvkm_therm_func *func) 436 { 437 nvkm_subdev_ctor(&nvkm_therm, device, index, &therm->subdev); 438 therm->func = func; 439 440 nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm); 441 spin_lock_init(&therm->lock); 442 spin_lock_init(&therm->sensor.alarm_program_lock); 443 444 therm->fan_get = nvkm_therm_fan_user_get; 445 therm->fan_set = nvkm_therm_fan_user_set; 446 therm->attr_get = nvkm_therm_attr_get; 447 therm->attr_set = nvkm_therm_attr_set; 448 therm->mode = therm->suspend = -1; /* undefined */ 449 450 therm->clkgating_enabled = nvkm_boolopt(device->cfgopt, 451 "NvPmEnableGating", false); 452 } 453 454 int 455 nvkm_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device, 456 int index, struct nvkm_therm **ptherm) 457 { 458 struct nvkm_therm *therm; 459 460 if (!(therm = *ptherm = kzalloc(sizeof(*therm), GFP_KERNEL))) 461 return -ENOMEM; 462 463 nvkm_therm_ctor(therm, device, index, func); 464 return 0; 465 } 466