diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 259 |
1 files changed, 221 insertions, 38 deletions
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 |