aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Peres <martin.peres@ensi-bourges.fr>2010-09-22 14:54:22 -0400
committerBen Skeggs <bskeggs@redhat.com>2010-09-24 02:28:28 -0400
commit34e9d85a1aae28b090ec4e72a0f98a5483c198c4 (patch)
treef7960b6f016447e19cbb14bab2c137366a67763b
parent64d202b4d78968979c0d44306854d41f9b71626d (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/Makefile2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c259
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_temp.c272
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
30nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o 30nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
31nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o 31nouveau-$(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
390struct 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
398struct nouveau_pm_threshold_temp {
399 s16 critical;
400 s16 down_clock;
401 s16 fan_boost;
402};
403
390struct nouveau_pm_engine { 404struct 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
668static inline struct drm_nouveau_private * 686static 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
30static int 33static int
31nouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz) 34nouveau_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,
189static ssize_t 192static ssize_t
190nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) 193nouveau_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
215nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, 218nouveau_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,
227DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, 230DEVICE_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
230int 233static int
231nouveau_pm_init(struct drm_device *dev) 234nouveau_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
293void 267static void
294nouveau_pm_fini(struct drm_device *dev) 268nouveau_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
288static ssize_t
289nouveau_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}
295static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
296 NULL, 0);
297
298static ssize_t
299nouveau_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}
308static ssize_t
309nouveau_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}
327static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
328 nouveau_hwmon_set_max_temp,
329 0);
330
331static ssize_t
332nouveau_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}
342static ssize_t
343nouveau_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}
362static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
363 nouveau_hwmon_critical_temp,
364 nouveau_hwmon_set_critical_temp,
365 0);
366
367static 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}
373static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
374
375static 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}
381static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
382 nouveau_hwmon_show_update_rate,
383 NULL, 0);
384
385static 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
394static const struct attribute_group hwmon_attrgroup = {
395 .attrs = hwmon_attributes,
396};
397
398static int
399nouveau_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
429static void
430nouveau_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
442int
443nouveau_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
484void
485nouveau_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
318void 501void
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);
52void *nv50_pm_clock_pre(struct drm_device *, u32 id, int khz); 52void *nv50_pm_clock_pre(struct drm_device *, u32 id, int khz);
53void nv50_pm_clock_set(struct drm_device *, void *); 53void nv50_pm_clock_set(struct drm_device *, void *);
54 54
55/* nouveau_temp.c */
56void nouveau_temp_init(struct drm_device *dev);
57void nouveau_temp_fini(struct drm_device *dev);
58void nouveau_temp_safety_checks(struct drm_device *dev);
59int16_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
30void
31nouveau_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
156static s16
157nouveau_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
184s16
185nouveau_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
220void
221nouveau_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
243void
244nouveau_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
268void
269nouveau_temp_fini(struct drm_device *dev)
270{
271
272}