xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/therm/nouveau_nvkm_subdev_therm_base.c (revision 798b8d11ecd8257a8e35c3396210f98abf3d9ade)
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
nvkm_therm_temp_get(struct nvkm_therm * therm)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
nvkm_therm_update_trip(struct nvkm_therm * therm)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
nvkm_therm_compute_linear_duty(struct nvkm_therm * therm,u8 linear_min_temp,u8 linear_max_temp)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
nvkm_therm_update_linear(struct nvkm_therm * therm)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
nvkm_therm_update_linear_fallback(struct nvkm_therm * therm)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
nvkm_therm_update(struct nvkm_therm * therm,int mode)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
nvkm_therm_cstate(struct nvkm_therm * therm,int fan,int dir)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
nvkm_therm_alarm(struct nvkm_alarm * alarm)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
nvkm_therm_fan_mode(struct nvkm_therm * therm,int mode)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
nvkm_therm_attr_get(struct nvkm_therm * therm,enum nvkm_therm_attr_type type)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
nvkm_therm_attr_set(struct nvkm_therm * therm,enum nvkm_therm_attr_type type,int value)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
nvkm_therm_clkgate_enable(struct nvkm_therm * therm)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
nvkm_therm_clkgate_fini(struct nvkm_therm * therm,bool suspend)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
nvkm_therm_clkgate_oneinit(struct nvkm_therm * therm)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
nvkm_therm_intr(struct nvkm_subdev * subdev)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
nvkm_therm_fini(struct nvkm_subdev * subdev,bool suspend)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
nvkm_therm_oneinit(struct nvkm_subdev * subdev)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
nvkm_therm_init(struct nvkm_subdev * subdev)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
nvkm_therm_clkgate_init(struct nvkm_therm * therm,const struct nvkm_therm_clkgate_pack * p)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 *
nvkm_therm_dtor(struct nvkm_subdev * subdev)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
nvkm_therm_ctor(struct nvkm_therm * therm,struct nvkm_device * device,int index,const struct nvkm_therm_func * func)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
nvkm_therm_new_(const struct nvkm_therm_func * func,struct nvkm_device * device,int index,struct nvkm_therm ** ptherm)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