diff options
author | Martin Peres <martin.peres@ensi-bourges.fr> | 2010-09-22 14:54:22 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-09-24 02:28:28 -0400 |
commit | 34e9d85a1aae28b090ec4e72a0f98a5483c198c4 (patch) | |
tree | f7960b6f016447e19cbb14bab2c137366a67763b | |
parent | 64d202b4d78968979c0d44306854d41f9b71626d (diff) |
drm/nouveau: Add temperature support (vbios parsing, readings, hwmon)
Signed-off-by: Martin Peres <martin.peres@ensi-bourges.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 259 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_temp.c | 272 |
5 files changed, 518 insertions, 39 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 3cedabeb1617..bdbde726778e 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile | |||
@@ -25,7 +25,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ | |||
25 | nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ | 25 | nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ |
26 | nv10_gpio.o nv50_gpio.o \ | 26 | nv10_gpio.o nv50_gpio.o \ |
27 | nv50_calc.o \ | 27 | nv50_calc.o \ |
28 | nv04_pm.o nv50_pm.o | 28 | nv04_pm.o nv50_pm.o nouveau_temp.o |
29 | 29 | ||
30 | nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o | 30 | nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o |
31 | nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o | 31 | nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 8d36ed6907d1..3fc5596df360 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -387,10 +387,26 @@ struct nouveau_pm_level { | |||
387 | u8 fanspeed; | 387 | u8 fanspeed; |
388 | }; | 388 | }; |
389 | 389 | ||
390 | struct nouveau_pm_temp_sensor_constants { | ||
391 | u16 offset_constant; | ||
392 | s16 offset_mult; | ||
393 | u16 offset_div; | ||
394 | u16 slope_mult; | ||
395 | u16 slope_div; | ||
396 | }; | ||
397 | |||
398 | struct nouveau_pm_threshold_temp { | ||
399 | s16 critical; | ||
400 | s16 down_clock; | ||
401 | s16 fan_boost; | ||
402 | }; | ||
403 | |||
390 | struct nouveau_pm_engine { | 404 | struct nouveau_pm_engine { |
391 | struct nouveau_pm_voltage voltage; | 405 | struct nouveau_pm_voltage voltage; |
392 | struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; | 406 | struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; |
393 | int nr_perflvl; | 407 | int nr_perflvl; |
408 | struct nouveau_pm_temp_sensor_constants sensor_constants; | ||
409 | struct nouveau_pm_threshold_temp threshold_temp; | ||
394 | 410 | ||
395 | struct nouveau_pm_level boot; | 411 | struct nouveau_pm_level boot; |
396 | struct nouveau_pm_level *cur; | 412 | struct nouveau_pm_level *cur; |
@@ -663,6 +679,8 @@ struct drm_nouveau_private { | |||
663 | 679 | ||
664 | struct nouveau_fbdev *nfbdev; | 680 | struct nouveau_fbdev *nfbdev; |
665 | struct apertures_struct *apertures; | 681 | struct apertures_struct *apertures; |
682 | |||
683 | struct device *int_hwmon_dev; | ||
666 | }; | 684 | }; |
667 | 685 | ||
668 | static inline struct drm_nouveau_private * | 686 | static inline struct drm_nouveau_private * |
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index a07f27447cf9..09b638435f8f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c | |||
@@ -27,6 +27,9 @@ | |||
27 | #include "nouveau_drv.h" | 27 | #include "nouveau_drv.h" |
28 | #include "nouveau_pm.h" | 28 | #include "nouveau_pm.h" |
29 | 29 | ||
30 | #include <linux/hwmon.h> | ||
31 | #include <linux/hwmon-sysfs.h> | ||
32 | |||
30 | static int | 33 | static int |
31 | nouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz) | 34 | nouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz) |
32 | { | 35 | { |
@@ -189,7 +192,7 @@ nouveau_pm_get_perflvl_info(struct device *d, | |||
189 | static ssize_t | 192 | static ssize_t |
190 | nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) | 193 | nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) |
191 | { | 194 | { |
192 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); | 195 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); |
193 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 196 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
194 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | 197 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; |
195 | struct nouveau_pm_level cur; | 198 | struct nouveau_pm_level cur; |
@@ -215,7 +218,7 @@ static ssize_t | |||
215 | nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, | 218 | nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, |
216 | const char *buf, size_t count) | 219 | const char *buf, size_t count) |
217 | { | 220 | { |
218 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); | 221 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); |
219 | int ret; | 222 | int ret; |
220 | 223 | ||
221 | ret = nouveau_pm_profile_set(dev, buf); | 224 | ret = nouveau_pm_profile_set(dev, buf); |
@@ -227,43 +230,14 @@ nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, | |||
227 | DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, | 230 | DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, |
228 | nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); | 231 | nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); |
229 | 232 | ||
230 | int | 233 | static int |
231 | nouveau_pm_init(struct drm_device *dev) | 234 | nouveau_sysfs_init(struct drm_device *dev) |
232 | { | 235 | { |
233 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 236 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
234 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | 237 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; |
235 | struct device *d = &dev->pdev->dev; | 238 | struct device *d = &dev->pdev->dev; |
236 | char info[256]; | ||
237 | int ret, i; | 239 | int ret, i; |
238 | 240 | ||
239 | nouveau_volt_init(dev); | ||
240 | nouveau_perf_init(dev); | ||
241 | |||
242 | NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); | ||
243 | for (i = 0; i < pm->nr_perflvl; i++) { | ||
244 | nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); | ||
245 | NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); | ||
246 | } | ||
247 | |||
248 | /* determine current ("boot") performance level */ | ||
249 | ret = nouveau_pm_perflvl_get(dev, &pm->boot); | ||
250 | if (ret == 0) { | ||
251 | pm->cur = &pm->boot; | ||
252 | |||
253 | nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); | ||
254 | NV_INFO(dev, "c: %s", info); | ||
255 | } | ||
256 | |||
257 | /* switch performance levels now if requested */ | ||
258 | if (nouveau_perflvl != NULL) { | ||
259 | ret = nouveau_pm_profile_set(dev, nouveau_perflvl); | ||
260 | if (ret) { | ||
261 | NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", | ||
262 | nouveau_perflvl, ret); | ||
263 | } | ||
264 | } | ||
265 | |||
266 | /* initialise sysfs */ | ||
267 | ret = device_create_file(d, &dev_attr_performance_level); | 241 | ret = device_create_file(d, &dev_attr_performance_level); |
268 | if (ret) | 242 | if (ret) |
269 | return ret; | 243 | return ret; |
@@ -290,17 +264,14 @@ nouveau_pm_init(struct drm_device *dev) | |||
290 | return 0; | 264 | return 0; |
291 | } | 265 | } |
292 | 266 | ||
293 | void | 267 | static void |
294 | nouveau_pm_fini(struct drm_device *dev) | 268 | nouveau_sysfs_fini(struct drm_device *dev) |
295 | { | 269 | { |
296 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 270 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
297 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | 271 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; |
298 | struct device *d = &dev->pdev->dev; | 272 | struct device *d = &dev->pdev->dev; |
299 | int i; | 273 | int i; |
300 | 274 | ||
301 | if (pm->cur != &pm->boot) | ||
302 | nouveau_pm_perflvl_set(dev, &pm->boot); | ||
303 | |||
304 | device_remove_file(d, &dev_attr_performance_level); | 275 | device_remove_file(d, &dev_attr_performance_level); |
305 | for (i = 0; i < pm->nr_perflvl; i++) { | 276 | for (i = 0; i < pm->nr_perflvl; i++) { |
306 | struct nouveau_pm_level *pl = &pm->perflvl[i]; | 277 | struct nouveau_pm_level *pl = &pm->perflvl[i]; |
@@ -310,9 +281,221 @@ nouveau_pm_fini(struct drm_device *dev) | |||
310 | 281 | ||
311 | device_remove_file(d, &pl->dev_attr); | 282 | device_remove_file(d, &pl->dev_attr); |
312 | } | 283 | } |
284 | } | ||
285 | |||
286 | |||
287 | |||
288 | static ssize_t | ||
289 | nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) | ||
290 | { | ||
291 | struct drm_device *dev = dev_get_drvdata(d); | ||
292 | |||
293 | return snprintf(buf, PAGE_SIZE, "%d\n", nouveau_temp_get(dev)*1000); | ||
294 | } | ||
295 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, | ||
296 | NULL, 0); | ||
297 | |||
298 | static ssize_t | ||
299 | nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) | ||
300 | { | ||
301 | struct drm_device *dev = dev_get_drvdata(d); | ||
302 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
303 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
304 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | ||
305 | |||
306 | return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000); | ||
307 | } | ||
308 | static ssize_t | ||
309 | nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, | ||
310 | const char *buf, size_t count) | ||
311 | { | ||
312 | struct drm_device *dev = dev_get_drvdata(d); | ||
313 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
314 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
315 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | ||
316 | long value; | ||
317 | |||
318 | if (strict_strtoul(buf, 10, &value) == -EINVAL) | ||
319 | return count; | ||
320 | |||
321 | temp->down_clock = value/1000; | ||
322 | |||
323 | nouveau_temp_safety_checks(dev); | ||
324 | |||
325 | return count; | ||
326 | } | ||
327 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, | ||
328 | nouveau_hwmon_set_max_temp, | ||
329 | 0); | ||
330 | |||
331 | static ssize_t | ||
332 | nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, | ||
333 | char *buf) | ||
334 | { | ||
335 | struct drm_device *dev = dev_get_drvdata(d); | ||
336 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
337 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
338 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | ||
339 | |||
340 | return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000); | ||
341 | } | ||
342 | static ssize_t | ||
343 | nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, | ||
344 | const char *buf, | ||
345 | size_t count) | ||
346 | { | ||
347 | struct drm_device *dev = dev_get_drvdata(d); | ||
348 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
349 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
350 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | ||
351 | long value; | ||
352 | |||
353 | if (strict_strtoul(buf, 10, &value) == -EINVAL) | ||
354 | return count; | ||
355 | |||
356 | temp->critical = value/1000; | ||
357 | |||
358 | nouveau_temp_safety_checks(dev); | ||
359 | |||
360 | return count; | ||
361 | } | ||
362 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, | ||
363 | nouveau_hwmon_critical_temp, | ||
364 | nouveau_hwmon_set_critical_temp, | ||
365 | 0); | ||
366 | |||
367 | static ssize_t nouveau_hwmon_show_name(struct device *dev, | ||
368 | struct device_attribute *attr, | ||
369 | char *buf) | ||
370 | { | ||
371 | return sprintf(buf, "nouveau\n"); | ||
372 | } | ||
373 | static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); | ||
374 | |||
375 | static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, | ||
376 | struct device_attribute *attr, | ||
377 | char *buf) | ||
378 | { | ||
379 | return sprintf(buf, "1000\n"); | ||
380 | } | ||
381 | static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, | ||
382 | nouveau_hwmon_show_update_rate, | ||
383 | NULL, 0); | ||
384 | |||
385 | static struct attribute *hwmon_attributes[] = { | ||
386 | &sensor_dev_attr_temp1_input.dev_attr.attr, | ||
387 | &sensor_dev_attr_temp1_max.dev_attr.attr, | ||
388 | &sensor_dev_attr_temp1_crit.dev_attr.attr, | ||
389 | &sensor_dev_attr_name.dev_attr.attr, | ||
390 | &sensor_dev_attr_update_rate.dev_attr.attr, | ||
391 | NULL | ||
392 | }; | ||
393 | |||
394 | static const struct attribute_group hwmon_attrgroup = { | ||
395 | .attrs = hwmon_attributes, | ||
396 | }; | ||
397 | |||
398 | static int | ||
399 | nouveau_hwmon_init(struct drm_device *dev) | ||
400 | { | ||
401 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
402 | struct device *hwmon_dev; | ||
403 | int ret; | ||
404 | |||
405 | dev_priv->int_hwmon_dev = NULL; | ||
406 | |||
407 | hwmon_dev = hwmon_device_register(&dev->pdev->dev); | ||
408 | if (IS_ERR(hwmon_dev)) { | ||
409 | ret = PTR_ERR(hwmon_dev); | ||
410 | NV_ERROR(dev, | ||
411 | "Unable to register hwmon device: %d\n", ret); | ||
412 | return ret; | ||
413 | } | ||
414 | dev_set_drvdata(hwmon_dev, dev); | ||
415 | ret = sysfs_create_group(&hwmon_dev->kobj, | ||
416 | &hwmon_attrgroup); | ||
417 | if (ret) { | ||
418 | NV_ERROR(dev, | ||
419 | "Unable to create hwmon sysfs file: %d\n", ret); | ||
420 | hwmon_device_unregister(hwmon_dev); | ||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | dev_priv->int_hwmon_dev = hwmon_dev; | ||
425 | |||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static void | ||
430 | nouveau_hwmon_fini(struct drm_device *dev) | ||
431 | { | ||
432 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
433 | |||
434 | if (dev_priv->int_hwmon_dev) { | ||
435 | sysfs_remove_group(&dev_priv->int_hwmon_dev->kobj, | ||
436 | &hwmon_attrgroup); | ||
437 | hwmon_device_unregister(dev_priv->int_hwmon_dev); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | |||
442 | int | ||
443 | nouveau_pm_init(struct drm_device *dev) | ||
444 | { | ||
445 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
446 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
447 | char info[256]; | ||
448 | int ret, i; | ||
449 | |||
450 | nouveau_volt_init(dev); | ||
451 | nouveau_perf_init(dev); | ||
452 | nouveau_temp_init(dev); | ||
453 | |||
454 | NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); | ||
455 | for (i = 0; i < pm->nr_perflvl; i++) { | ||
456 | nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); | ||
457 | NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); | ||
458 | } | ||
459 | |||
460 | /* determine current ("boot") performance level */ | ||
461 | ret = nouveau_pm_perflvl_get(dev, &pm->boot); | ||
462 | if (ret == 0) { | ||
463 | pm->cur = &pm->boot; | ||
464 | |||
465 | nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); | ||
466 | NV_INFO(dev, "c: %s", info); | ||
467 | } | ||
468 | |||
469 | /* switch performance levels now if requested */ | ||
470 | if (nouveau_perflvl != NULL) { | ||
471 | ret = nouveau_pm_profile_set(dev, nouveau_perflvl); | ||
472 | if (ret) { | ||
473 | NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", | ||
474 | nouveau_perflvl, ret); | ||
475 | } | ||
476 | } | ||
477 | |||
478 | nouveau_sysfs_init(dev); | ||
479 | nouveau_hwmon_init(dev); | ||
480 | |||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | void | ||
485 | nouveau_pm_fini(struct drm_device *dev) | ||
486 | { | ||
487 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
488 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
489 | |||
490 | if (pm->cur != &pm->boot) | ||
491 | nouveau_pm_perflvl_set(dev, &pm->boot); | ||
313 | 492 | ||
314 | nouveau_perf_fini(dev); | 493 | nouveau_perf_fini(dev); |
315 | nouveau_volt_fini(dev); | 494 | nouveau_volt_fini(dev); |
495 | nouveau_temp_fini(dev); | ||
496 | |||
497 | nouveau_hwmon_fini(dev); | ||
498 | nouveau_sysfs_fini(dev); | ||
316 | } | 499 | } |
317 | 500 | ||
318 | void | 501 | void |
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index f3de5a68c41f..d048b7516b1c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h | |||
@@ -52,4 +52,10 @@ int nv50_pm_clock_get(struct drm_device *, u32 id); | |||
52 | void *nv50_pm_clock_pre(struct drm_device *, u32 id, int khz); | 52 | void *nv50_pm_clock_pre(struct drm_device *, u32 id, int khz); |
53 | void nv50_pm_clock_set(struct drm_device *, void *); | 53 | void nv50_pm_clock_set(struct drm_device *, void *); |
54 | 54 | ||
55 | /* nouveau_temp.c */ | ||
56 | void nouveau_temp_init(struct drm_device *dev); | ||
57 | void nouveau_temp_fini(struct drm_device *dev); | ||
58 | void nouveau_temp_safety_checks(struct drm_device *dev); | ||
59 | int16_t nouveau_temp_get(struct drm_device *dev); | ||
60 | |||
55 | #endif | 61 | #endif |
diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c new file mode 100644 index 000000000000..3394075e4c3c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * Copyright 2010 PathScale inc. | ||
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 | |||
25 | #include "drmP.h" | ||
26 | |||
27 | #include "nouveau_drv.h" | ||
28 | #include "nouveau_pm.h" | ||
29 | |||
30 | void | ||
31 | nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) | ||
32 | { | ||
33 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
34 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
35 | struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; | ||
36 | struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; | ||
37 | int i, headerlen, recordlen, entries; | ||
38 | |||
39 | if (!temp) { | ||
40 | NV_DEBUG(dev, "temperature table pointer invalid\n"); | ||
41 | return; | ||
42 | } | ||
43 | |||
44 | /* Set the default sensor's contants */ | ||
45 | sensor->offset_constant = 0; | ||
46 | sensor->offset_mult = 1; | ||
47 | sensor->offset_div = 1; | ||
48 | sensor->slope_mult = 1; | ||
49 | sensor->slope_div = 1; | ||
50 | |||
51 | /* Set the default temperature thresholds */ | ||
52 | temps->critical = 110; | ||
53 | temps->down_clock = 100; | ||
54 | temps->fan_boost = 90; | ||
55 | |||
56 | /* Set the known default values to setup the temperature sensor */ | ||
57 | if (dev_priv->card_type >= NV_40) { | ||
58 | switch (dev_priv->chipset) { | ||
59 | case 0x43: | ||
60 | sensor->offset_mult = 32060; | ||
61 | sensor->offset_div = 1000; | ||
62 | sensor->slope_mult = 792; | ||
63 | sensor->slope_div = 1000; | ||
64 | break; | ||
65 | |||
66 | case 0x44: | ||
67 | case 0x47: | ||
68 | sensor->offset_mult = 27839; | ||
69 | sensor->offset_div = 1000; | ||
70 | sensor->slope_mult = 780; | ||
71 | sensor->slope_div = 1000; | ||
72 | break; | ||
73 | |||
74 | case 0x46: | ||
75 | sensor->offset_mult = -24775; | ||
76 | sensor->offset_div = 100; | ||
77 | sensor->slope_mult = 467; | ||
78 | sensor->slope_div = 10000; | ||
79 | break; | ||
80 | |||
81 | case 0x49: | ||
82 | sensor->offset_mult = -25051; | ||
83 | sensor->offset_div = 100; | ||
84 | sensor->slope_mult = 458; | ||
85 | sensor->slope_div = 10000; | ||
86 | break; | ||
87 | |||
88 | case 0x4b: | ||
89 | sensor->offset_mult = -24088; | ||
90 | sensor->offset_div = 100; | ||
91 | sensor->slope_mult = 442; | ||
92 | sensor->slope_div = 10000; | ||
93 | break; | ||
94 | |||
95 | case 0x50: | ||
96 | sensor->offset_mult = -22749; | ||
97 | sensor->offset_div = 100; | ||
98 | sensor->slope_mult = 431; | ||
99 | sensor->slope_div = 10000; | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | headerlen = temp[1]; | ||
105 | recordlen = temp[2]; | ||
106 | entries = temp[3]; | ||
107 | temp = temp + headerlen; | ||
108 | |||
109 | /* Read the entries from the table */ | ||
110 | for (i = 0; i < entries; i++) { | ||
111 | u16 value = ROM16(temp[1]); | ||
112 | |||
113 | switch (temp[0]) { | ||
114 | case 0x01: | ||
115 | value = (value&0x8f) == 0 ? (value >> 9) & 0x7f : 0; | ||
116 | sensor->offset_constant = value; | ||
117 | break; | ||
118 | |||
119 | case 0x04: | ||
120 | if ((value & 0xf00f) == 0xa000) /* core */ | ||
121 | temps->critical = (value&0x0ff0) >> 4; | ||
122 | break; | ||
123 | |||
124 | case 0x07: | ||
125 | if ((value & 0xf00f) == 0xa000) /* core */ | ||
126 | temps->down_clock = (value&0x0ff0) >> 4; | ||
127 | break; | ||
128 | |||
129 | case 0x08: | ||
130 | if ((value & 0xf00f) == 0xa000) /* core */ | ||
131 | temps->fan_boost = (value&0x0ff0) >> 4; | ||
132 | break; | ||
133 | |||
134 | case 0x10: | ||
135 | sensor->offset_mult = value; | ||
136 | break; | ||
137 | |||
138 | case 0x11: | ||
139 | sensor->offset_div = value; | ||
140 | break; | ||
141 | |||
142 | case 0x12: | ||
143 | sensor->slope_mult = value; | ||
144 | break; | ||
145 | |||
146 | case 0x13: | ||
147 | sensor->slope_div = value; | ||
148 | break; | ||
149 | } | ||
150 | temp += recordlen; | ||
151 | } | ||
152 | |||
153 | nouveau_temp_safety_checks(dev); | ||
154 | } | ||
155 | |||
156 | static s16 | ||
157 | nouveau_nv40_sensor_setup(struct drm_device *dev) | ||
158 | { | ||
159 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
160 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
161 | struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; | ||
162 | u32 offset = sensor->offset_mult / sensor->offset_div; | ||
163 | u32 sensor_calibration; | ||
164 | |||
165 | /* set up the sensors */ | ||
166 | sensor_calibration = 120 - offset - sensor->offset_constant; | ||
167 | sensor_calibration = sensor_calibration * sensor->slope_div / | ||
168 | sensor->slope_mult; | ||
169 | |||
170 | if (dev_priv->chipset >= 0x46) | ||
171 | sensor_calibration |= 0x80000000; | ||
172 | else | ||
173 | sensor_calibration |= 0x10000000; | ||
174 | |||
175 | nv_wr32(dev, 0x0015b0, sensor_calibration); | ||
176 | |||
177 | /* Wait for the sensor to update */ | ||
178 | msleep(5); | ||
179 | |||
180 | /* read */ | ||
181 | return nv_rd32(dev, 0x0015b4); | ||
182 | } | ||
183 | |||
184 | s16 | ||
185 | nouveau_temp_get(struct drm_device *dev) | ||
186 | { | ||
187 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
188 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
189 | struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; | ||
190 | |||
191 | if (dev_priv->chipset >= 0x84) { | ||
192 | return nv_rd32(dev, 0x20400); | ||
193 | } else if (dev_priv->chipset >= 0x40) { | ||
194 | u32 offset = sensor->offset_mult / sensor->offset_div; | ||
195 | u32 core_temp; | ||
196 | |||
197 | if (dev_priv->chipset >= 0x50) { | ||
198 | core_temp = nv_rd32(dev, 0x20008); | ||
199 | } else { | ||
200 | core_temp = nv_rd32(dev, 0x0015b4); | ||
201 | /* Setup the sensor if the temperature is 0 */ | ||
202 | if (core_temp == 0) | ||
203 | core_temp = nouveau_nv40_sensor_setup(dev); | ||
204 | } | ||
205 | |||
206 | core_temp = core_temp * sensor->slope_mult / sensor->slope_div; | ||
207 | core_temp = core_temp + offset + sensor->offset_constant; | ||
208 | |||
209 | return core_temp; | ||
210 | } else { | ||
211 | NV_ERROR(dev, | ||
212 | "Temperature cannot be retrieved from an nv%x card\n", | ||
213 | dev_priv->chipset); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | void | ||
221 | nouveau_temp_safety_checks(struct drm_device *dev) | ||
222 | { | ||
223 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
224 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
225 | struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; | ||
226 | |||
227 | if (temps->critical > 120) | ||
228 | temps->critical = 120; | ||
229 | else if (temps->critical < 80) | ||
230 | temps->critical = 80; | ||
231 | |||
232 | if (temps->down_clock > 110) | ||
233 | temps->down_clock = 110; | ||
234 | else if (temps->down_clock < 60) | ||
235 | temps->down_clock = 60; | ||
236 | |||
237 | if (temps->fan_boost > 100) | ||
238 | temps->fan_boost = 100; | ||
239 | else if (temps->fan_boost < 40) | ||
240 | temps->fan_boost = 40; | ||
241 | } | ||
242 | |||
243 | void | ||
244 | nouveau_temp_init(struct drm_device *dev) | ||
245 | { | ||
246 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
247 | struct nvbios *bios = &dev_priv->vbios; | ||
248 | struct bit_entry P; | ||
249 | u8 *temp = NULL; | ||
250 | |||
251 | if (bios->type == NVBIOS_BIT) { | ||
252 | if (bit_table(dev, 'P', &P)) | ||
253 | return; | ||
254 | |||
255 | if (P.version == 1) | ||
256 | temp = ROMPTR(bios, P.data[12]); | ||
257 | else if (P.version == 2) | ||
258 | temp = ROMPTR(bios, P.data[16]); | ||
259 | else | ||
260 | NV_WARN(dev, "unknown temp for BIT P %d\n", P.version); | ||
261 | } else { | ||
262 | NV_WARN(dev, "BMP entry unknown for temperature table.\n"); | ||
263 | } | ||
264 | |||
265 | nouveau_temp_vbios_parse(dev, temp); | ||
266 | } | ||
267 | |||
268 | void | ||
269 | nouveau_temp_fini(struct drm_device *dev) | ||
270 | { | ||
271 | |||
272 | } | ||