1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "priv.h"
25
26int
27nvkm_therm_temp_get(struct nvkm_therm *therm)
28{
29 if (therm->func->temp_get)
30 return therm->func->temp_get(therm);
31 return -ENODEV;
32}
33
34static int
35nvkm_therm_update_trip(struct nvkm_therm *therm)
36{
37 struct nvbios_therm_trip_point *trip = therm->fan->bios.trip,
38 *cur_trip = NULL,
39 *last_trip = therm->last_trip;
40 u8 temp = therm->func->temp_get(therm);
41 u16 duty, i;
42
43
44 cur_trip = NULL;
45 for (i = 0; i < therm->fan->bios.nr_fan_trip; i++) {
46 if (temp >= trip[i].temp)
47 cur_trip = &trip[i];
48 }
49
50
51 if (last_trip && temp <= (last_trip->temp) &&
52 temp > (last_trip->temp - last_trip->hysteresis))
53 cur_trip = last_trip;
54
55 if (cur_trip) {
56 duty = cur_trip->fan_duty;
57 therm->last_trip = cur_trip;
58 } else {
59 duty = 0;
60 therm->last_trip = NULL;
61 }
62
63 return duty;
64}
65
66static int
67nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8 linear_min_temp,
68 u8 linear_max_temp)
69{
70 u8 temp = therm->func->temp_get(therm);
71 u16 duty;
72
73
74 if (temp < linear_min_temp)
75 return therm->fan->bios.min_duty;
76 else if (temp > linear_max_temp)
77 return therm->fan->bios.max_duty;
78
79
80 duty = (temp - linear_min_temp);
81 duty *= (therm->fan->bios.max_duty - therm->fan->bios.min_duty);
82 duty /= (linear_max_temp - linear_min_temp);
83 duty += therm->fan->bios.min_duty;
84 return duty;
85}
86
87static int
88nvkm_therm_update_linear(struct nvkm_therm *therm)
89{
90 u8 min = therm->fan->bios.linear_min_temp;
91 u8 max = therm->fan->bios.linear_max_temp;
92 return nvkm_therm_compute_linear_duty(therm, min, max);
93}
94
95static int
96nvkm_therm_update_linear_fallback(struct nvkm_therm *therm)
97{
98 u8 max = therm->bios_sensor.thrs_fan_boost.temp;
99 return nvkm_therm_compute_linear_duty(therm, 30, max);
100}
101
102static void
103nvkm_therm_update(struct nvkm_therm *therm, int mode)
104{
105 struct nvkm_subdev *subdev = &therm->subdev;
106 struct nvkm_timer *tmr = subdev->device->timer;
107 unsigned long flags;
108 bool immd = true;
109 bool poll = true;
110 int duty = -1;
111
112 spin_lock_irqsave(&therm->lock, flags);
113 if (mode < 0)
114 mode = therm->mode;
115 therm->mode = mode;
116
117 switch (mode) {
118 case NVKM_THERM_CTRL_MANUAL:
119 nvkm_timer_alarm_cancel(tmr, &therm->alarm);
120 duty = nvkm_therm_fan_get(therm);
121 if (duty < 0)
122 duty = 100;
123 poll = false;
124 break;
125 case NVKM_THERM_CTRL_AUTO:
126 switch(therm->fan->bios.fan_mode) {
127 case NVBIOS_THERM_FAN_TRIP:
128 duty = nvkm_therm_update_trip(therm);
129 break;
130 case NVBIOS_THERM_FAN_LINEAR:
131 duty = nvkm_therm_update_linear(therm);
132 break;
133 case NVBIOS_THERM_FAN_OTHER:
134 if (therm->cstate)
135 duty = therm->cstate;
136 else
137 duty = nvkm_therm_update_linear_fallback(therm);
138 poll = false;
139 break;
140 }
141 immd = false;
142 break;
143 case NVKM_THERM_CTRL_NONE:
144 default:
145 nvkm_timer_alarm_cancel(tmr, &therm->alarm);
146 poll = false;
147 }
148
149 if (list_empty(&therm->alarm.head) && poll)
150 nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm);
151 spin_unlock_irqrestore(&therm->lock, flags);
152
153 if (duty >= 0) {
154 nvkm_debug(subdev, "FAN target request: %d%%\n", duty);
155 nvkm_therm_fan_set(therm, immd, duty);
156 }
157}
158
159int
160nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int dir)
161{
162 struct nvkm_subdev *subdev = &therm->subdev;
163 if (!dir || (dir < 0 && fan < therm->cstate) ||
164 (dir > 0 && fan > therm->cstate)) {
165 nvkm_debug(subdev, "default fan speed -> %d%%\n", fan);
166 therm->cstate = fan;
167 nvkm_therm_update(therm, -1);
168 }
169 return 0;
170}
171
172static void
173nvkm_therm_alarm(struct nvkm_alarm *alarm)
174{
175 struct nvkm_therm *therm =
176 container_of(alarm, struct nvkm_therm, alarm);
177 nvkm_therm_update(therm, -1);
178}
179
180int
181nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
182{
183 struct nvkm_subdev *subdev = &therm->subdev;
184 struct nvkm_device *device = subdev->device;
185 static const char *name[] = {
186 "disabled",
187 "manual",
188 "automatic"
189 };
190
191
192 if ((mode >= ARRAY_SIZE(name)) ||
193 (mode != NVKM_THERM_CTRL_NONE && device->card_type >= NV_C0 &&
194 !device->pmu))
195 return -EINVAL;
196
197
198
199 if (mode == NVKM_THERM_CTRL_AUTO &&
200 therm->func->temp_get(therm) < 0)
201 return -EINVAL;
202
203 if (therm->mode == mode)
204 return 0;
205
206 nvkm_debug(subdev, "fan management: %s\n", name[mode]);
207 nvkm_therm_update(therm, mode);
208 return 0;
209}
210
211int
212nvkm_therm_attr_get(struct nvkm_therm *therm, enum nvkm_therm_attr_type type)
213{
214 switch (type) {
215 case NVKM_THERM_ATTR_FAN_MIN_DUTY:
216 return therm->fan->bios.min_duty;
217 case NVKM_THERM_ATTR_FAN_MAX_DUTY:
218 return therm->fan->bios.max_duty;
219 case NVKM_THERM_ATTR_FAN_MODE:
220 return therm->mode;
221 case NVKM_THERM_ATTR_THRS_FAN_BOOST:
222 return therm->bios_sensor.thrs_fan_boost.temp;
223 case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
224 return therm->bios_sensor.thrs_fan_boost.hysteresis;
225 case NVKM_THERM_ATTR_THRS_DOWN_CLK:
226 return therm->bios_sensor.thrs_down_clock.temp;
227 case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
228 return therm->bios_sensor.thrs_down_clock.hysteresis;
229 case NVKM_THERM_ATTR_THRS_CRITICAL:
230 return therm->bios_sensor.thrs_critical.temp;
231 case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
232 return therm->bios_sensor.thrs_critical.hysteresis;
233 case NVKM_THERM_ATTR_THRS_SHUTDOWN:
234 return therm->bios_sensor.thrs_shutdown.temp;
235 case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
236 return therm->bios_sensor.thrs_shutdown.hysteresis;
237 }
238
239 return -EINVAL;
240}
241
242int
243nvkm_therm_attr_set(struct nvkm_therm *therm,
244 enum nvkm_therm_attr_type type, int value)
245{
246 switch (type) {
247 case NVKM_THERM_ATTR_FAN_MIN_DUTY:
248 if (value < 0)
249 value = 0;
250 if (value > therm->fan->bios.max_duty)
251 value = therm->fan->bios.max_duty;
252 therm->fan->bios.min_duty = value;
253 return 0;
254 case NVKM_THERM_ATTR_FAN_MAX_DUTY:
255 if (value < 0)
256 value = 0;
257 if (value < therm->fan->bios.min_duty)
258 value = therm->fan->bios.min_duty;
259 therm->fan->bios.max_duty = value;
260 return 0;
261 case NVKM_THERM_ATTR_FAN_MODE:
262 return nvkm_therm_fan_mode(therm, value);
263 case NVKM_THERM_ATTR_THRS_FAN_BOOST:
264 therm->bios_sensor.thrs_fan_boost.temp = value;
265 therm->func->program_alarms(therm);
266 return 0;
267 case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
268 therm->bios_sensor.thrs_fan_boost.hysteresis = value;
269 therm->func->program_alarms(therm);
270 return 0;
271 case NVKM_THERM_ATTR_THRS_DOWN_CLK:
272 therm->bios_sensor.thrs_down_clock.temp = value;
273 therm->func->program_alarms(therm);
274 return 0;
275 case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
276 therm->bios_sensor.thrs_down_clock.hysteresis = value;
277 therm->func->program_alarms(therm);
278 return 0;
279 case NVKM_THERM_ATTR_THRS_CRITICAL:
280 therm->bios_sensor.thrs_critical.temp = value;
281 therm->func->program_alarms(therm);
282 return 0;
283 case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
284 therm->bios_sensor.thrs_critical.hysteresis = value;
285 therm->func->program_alarms(therm);
286 return 0;
287 case NVKM_THERM_ATTR_THRS_SHUTDOWN:
288 therm->bios_sensor.thrs_shutdown.temp = value;
289 therm->func->program_alarms(therm);
290 return 0;
291 case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
292 therm->bios_sensor.thrs_shutdown.hysteresis = value;
293 therm->func->program_alarms(therm);
294 return 0;
295 }
296
297 return -EINVAL;
298}
299
300static void
301nvkm_therm_intr(struct nvkm_subdev *subdev)
302{
303 struct nvkm_therm *therm = nvkm_therm(subdev);
304 if (therm->func->intr)
305 therm->func->intr(therm);
306}
307
308static int
309nvkm_therm_fini(struct nvkm_subdev *subdev, bool suspend)
310{
311 struct nvkm_therm *therm = nvkm_therm(subdev);
312
313 if (therm->func->fini)
314 therm->func->fini(therm);
315
316 nvkm_therm_fan_fini(therm, suspend);
317 nvkm_therm_sensor_fini(therm, suspend);
318
319 if (suspend) {
320 therm->suspend = therm->mode;
321 therm->mode = NVKM_THERM_CTRL_NONE;
322 }
323
324 return 0;
325}
326
327static int
328nvkm_therm_oneinit(struct nvkm_subdev *subdev)
329{
330 struct nvkm_therm *therm = nvkm_therm(subdev);
331 nvkm_therm_sensor_ctor(therm);
332 nvkm_therm_ic_ctor(therm);
333 nvkm_therm_fan_ctor(therm);
334 nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
335 nvkm_therm_sensor_preinit(therm);
336 return 0;
337}
338
339static int
340nvkm_therm_init(struct nvkm_subdev *subdev)
341{
342 struct nvkm_therm *therm = nvkm_therm(subdev);
343
344 therm->func->init(therm);
345
346 if (therm->suspend >= 0) {
347
348 if (therm->suspend > 0)
349 nvkm_therm_fan_set(therm, true, therm->fan->percent);
350
351 nvkm_therm_fan_mode(therm, therm->suspend);
352 }
353
354 nvkm_therm_sensor_init(therm);
355 nvkm_therm_fan_init(therm);
356 return 0;
357}
358
359static void *
360nvkm_therm_dtor(struct nvkm_subdev *subdev)
361{
362 struct nvkm_therm *therm = nvkm_therm(subdev);
363 kfree(therm->fan);
364 return therm;
365}
366
367static const struct nvkm_subdev_func
368nvkm_therm = {
369 .dtor = nvkm_therm_dtor,
370 .oneinit = nvkm_therm_oneinit,
371 .init = nvkm_therm_init,
372 .fini = nvkm_therm_fini,
373 .intr = nvkm_therm_intr,
374};
375
376int
377nvkm_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device,
378 int index, struct nvkm_therm **ptherm)
379{
380 struct nvkm_therm *therm;
381
382 if (!(therm = *ptherm = kzalloc(sizeof(*therm), GFP_KERNEL)))
383 return -ENOMEM;
384
385 nvkm_subdev_ctor(&nvkm_therm, device, index, &therm->subdev);
386 therm->func = func;
387
388 nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm);
389 spin_lock_init(&therm->lock);
390 spin_lock_init(&therm->sensor.alarm_program_lock);
391
392 therm->fan_get = nvkm_therm_fan_user_get;
393 therm->fan_set = nvkm_therm_fan_user_set;
394 therm->attr_get = nvkm_therm_attr_get;
395 therm->attr_set = nvkm_therm_attr_set;
396 therm->mode = therm->suspend = -1;
397 return 0;
398}
399