Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpu/drm/nouveau/nouveau_pm.c
15112 views
1
/*
2
* Copyright 2010 Red Hat 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: Ben Skeggs
23
*/
24
25
#include "drmP.h"
26
27
#include "nouveau_drv.h"
28
#include "nouveau_pm.h"
29
30
#ifdef CONFIG_ACPI
31
#include <linux/acpi.h>
32
#endif
33
#include <linux/power_supply.h>
34
#include <linux/hwmon.h>
35
#include <linux/hwmon-sysfs.h>
36
37
static int
38
nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,
39
u8 id, u32 khz)
40
{
41
struct drm_nouveau_private *dev_priv = dev->dev_private;
42
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
43
void *pre_state;
44
45
if (khz == 0)
46
return 0;
47
48
pre_state = pm->clock_pre(dev, perflvl, id, khz);
49
if (IS_ERR(pre_state))
50
return PTR_ERR(pre_state);
51
52
if (pre_state)
53
pm->clock_set(dev, pre_state);
54
return 0;
55
}
56
57
static int
58
nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
59
{
60
struct drm_nouveau_private *dev_priv = dev->dev_private;
61
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
62
int ret;
63
64
if (perflvl == pm->cur)
65
return 0;
66
67
if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
68
ret = pm->voltage_set(dev, perflvl->voltage);
69
if (ret) {
70
NV_ERROR(dev, "voltage_set %d failed: %d\n",
71
perflvl->voltage, ret);
72
}
73
}
74
75
nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
76
nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
77
nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
78
nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
79
80
pm->cur = perflvl;
81
return 0;
82
}
83
84
static int
85
nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
86
{
87
struct drm_nouveau_private *dev_priv = dev->dev_private;
88
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
89
struct nouveau_pm_level *perflvl = NULL;
90
91
/* safety precaution, for now */
92
if (nouveau_perflvl_wr != 7777)
93
return -EPERM;
94
95
if (!pm->clock_set)
96
return -EINVAL;
97
98
if (!strncmp(profile, "boot", 4))
99
perflvl = &pm->boot;
100
else {
101
int pl = simple_strtol(profile, NULL, 10);
102
int i;
103
104
for (i = 0; i < pm->nr_perflvl; i++) {
105
if (pm->perflvl[i].id == pl) {
106
perflvl = &pm->perflvl[i];
107
break;
108
}
109
}
110
111
if (!perflvl)
112
return -EINVAL;
113
}
114
115
NV_INFO(dev, "setting performance level: %s\n", profile);
116
return nouveau_pm_perflvl_set(dev, perflvl);
117
}
118
119
static int
120
nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
121
{
122
struct drm_nouveau_private *dev_priv = dev->dev_private;
123
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
124
int ret;
125
126
if (!pm->clock_get)
127
return -EINVAL;
128
129
memset(perflvl, 0, sizeof(*perflvl));
130
131
ret = pm->clock_get(dev, PLL_CORE);
132
if (ret > 0)
133
perflvl->core = ret;
134
135
ret = pm->clock_get(dev, PLL_MEMORY);
136
if (ret > 0)
137
perflvl->memory = ret;
138
139
ret = pm->clock_get(dev, PLL_SHADER);
140
if (ret > 0)
141
perflvl->shader = ret;
142
143
ret = pm->clock_get(dev, PLL_UNK05);
144
if (ret > 0)
145
perflvl->unk05 = ret;
146
147
if (pm->voltage.supported && pm->voltage_get) {
148
ret = pm->voltage_get(dev);
149
if (ret > 0)
150
perflvl->voltage = ret;
151
}
152
153
return 0;
154
}
155
156
static void
157
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
158
{
159
char c[16], s[16], v[16], f[16], t[16];
160
161
c[0] = '\0';
162
if (perflvl->core)
163
snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
164
165
s[0] = '\0';
166
if (perflvl->shader)
167
snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
168
169
v[0] = '\0';
170
if (perflvl->voltage)
171
snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);
172
173
f[0] = '\0';
174
if (perflvl->fanspeed)
175
snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
176
177
t[0] = '\0';
178
if (perflvl->timing)
179
snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
180
181
snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000,
182
c, s, v, f, t);
183
}
184
185
static ssize_t
186
nouveau_pm_get_perflvl_info(struct device *d,
187
struct device_attribute *a, char *buf)
188
{
189
struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
190
char *ptr = buf;
191
int len = PAGE_SIZE;
192
193
snprintf(ptr, len, "%d: ", perflvl->id);
194
ptr += strlen(buf);
195
len -= strlen(buf);
196
197
nouveau_pm_perflvl_info(perflvl, ptr, len);
198
return strlen(buf);
199
}
200
201
static ssize_t
202
nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
203
{
204
struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
205
struct drm_nouveau_private *dev_priv = dev->dev_private;
206
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
207
struct nouveau_pm_level cur;
208
int len = PAGE_SIZE, ret;
209
char *ptr = buf;
210
211
if (!pm->cur)
212
snprintf(ptr, len, "setting: boot\n");
213
else if (pm->cur == &pm->boot)
214
snprintf(ptr, len, "setting: boot\nc: ");
215
else
216
snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id);
217
ptr += strlen(buf);
218
len -= strlen(buf);
219
220
ret = nouveau_pm_perflvl_get(dev, &cur);
221
if (ret == 0)
222
nouveau_pm_perflvl_info(&cur, ptr, len);
223
return strlen(buf);
224
}
225
226
static ssize_t
227
nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
228
const char *buf, size_t count)
229
{
230
struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
231
int ret;
232
233
ret = nouveau_pm_profile_set(dev, buf);
234
if (ret)
235
return ret;
236
return strlen(buf);
237
}
238
239
static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
240
nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
241
242
static int
243
nouveau_sysfs_init(struct drm_device *dev)
244
{
245
struct drm_nouveau_private *dev_priv = dev->dev_private;
246
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
247
struct device *d = &dev->pdev->dev;
248
int ret, i;
249
250
ret = device_create_file(d, &dev_attr_performance_level);
251
if (ret)
252
return ret;
253
254
for (i = 0; i < pm->nr_perflvl; i++) {
255
struct nouveau_pm_level *perflvl = &pm->perflvl[i];
256
257
perflvl->dev_attr.attr.name = perflvl->name;
258
perflvl->dev_attr.attr.mode = S_IRUGO;
259
perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
260
perflvl->dev_attr.store = NULL;
261
sysfs_attr_init(&perflvl->dev_attr.attr);
262
263
ret = device_create_file(d, &perflvl->dev_attr);
264
if (ret) {
265
NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
266
perflvl->id, i);
267
perflvl->dev_attr.attr.name = NULL;
268
nouveau_pm_fini(dev);
269
return ret;
270
}
271
}
272
273
return 0;
274
}
275
276
static void
277
nouveau_sysfs_fini(struct drm_device *dev)
278
{
279
struct drm_nouveau_private *dev_priv = dev->dev_private;
280
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
281
struct device *d = &dev->pdev->dev;
282
int i;
283
284
device_remove_file(d, &dev_attr_performance_level);
285
for (i = 0; i < pm->nr_perflvl; i++) {
286
struct nouveau_pm_level *pl = &pm->perflvl[i];
287
288
if (!pl->dev_attr.attr.name)
289
break;
290
291
device_remove_file(d, &pl->dev_attr);
292
}
293
}
294
295
#ifdef CONFIG_HWMON
296
static ssize_t
297
nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
298
{
299
struct drm_device *dev = dev_get_drvdata(d);
300
struct drm_nouveau_private *dev_priv = dev->dev_private;
301
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
302
303
return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
304
}
305
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
306
NULL, 0);
307
308
static ssize_t
309
nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
310
{
311
struct drm_device *dev = dev_get_drvdata(d);
312
struct drm_nouveau_private *dev_priv = dev->dev_private;
313
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
314
struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
315
316
return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
317
}
318
static ssize_t
319
nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
320
const char *buf, size_t count)
321
{
322
struct drm_device *dev = dev_get_drvdata(d);
323
struct drm_nouveau_private *dev_priv = dev->dev_private;
324
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
325
struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
326
long value;
327
328
if (strict_strtol(buf, 10, &value) == -EINVAL)
329
return count;
330
331
temp->down_clock = value/1000;
332
333
nouveau_temp_safety_checks(dev);
334
335
return count;
336
}
337
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
338
nouveau_hwmon_set_max_temp,
339
0);
340
341
static ssize_t
342
nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
343
char *buf)
344
{
345
struct drm_device *dev = dev_get_drvdata(d);
346
struct drm_nouveau_private *dev_priv = dev->dev_private;
347
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
348
struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
349
350
return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
351
}
352
static ssize_t
353
nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
354
const char *buf,
355
size_t count)
356
{
357
struct drm_device *dev = dev_get_drvdata(d);
358
struct drm_nouveau_private *dev_priv = dev->dev_private;
359
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
360
struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
361
long value;
362
363
if (strict_strtol(buf, 10, &value) == -EINVAL)
364
return count;
365
366
temp->critical = value/1000;
367
368
nouveau_temp_safety_checks(dev);
369
370
return count;
371
}
372
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
373
nouveau_hwmon_critical_temp,
374
nouveau_hwmon_set_critical_temp,
375
0);
376
377
static ssize_t nouveau_hwmon_show_name(struct device *dev,
378
struct device_attribute *attr,
379
char *buf)
380
{
381
return sprintf(buf, "nouveau\n");
382
}
383
static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
384
385
static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
386
struct device_attribute *attr,
387
char *buf)
388
{
389
return sprintf(buf, "1000\n");
390
}
391
static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
392
nouveau_hwmon_show_update_rate,
393
NULL, 0);
394
395
static struct attribute *hwmon_attributes[] = {
396
&sensor_dev_attr_temp1_input.dev_attr.attr,
397
&sensor_dev_attr_temp1_max.dev_attr.attr,
398
&sensor_dev_attr_temp1_crit.dev_attr.attr,
399
&sensor_dev_attr_name.dev_attr.attr,
400
&sensor_dev_attr_update_rate.dev_attr.attr,
401
NULL
402
};
403
404
static const struct attribute_group hwmon_attrgroup = {
405
.attrs = hwmon_attributes,
406
};
407
#endif
408
409
static int
410
nouveau_hwmon_init(struct drm_device *dev)
411
{
412
#ifdef CONFIG_HWMON
413
struct drm_nouveau_private *dev_priv = dev->dev_private;
414
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
415
struct device *hwmon_dev;
416
int ret;
417
418
if (!pm->temp_get)
419
return -ENODEV;
420
421
hwmon_dev = hwmon_device_register(&dev->pdev->dev);
422
if (IS_ERR(hwmon_dev)) {
423
ret = PTR_ERR(hwmon_dev);
424
NV_ERROR(dev,
425
"Unable to register hwmon device: %d\n", ret);
426
return ret;
427
}
428
dev_set_drvdata(hwmon_dev, dev);
429
ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
430
if (ret) {
431
NV_ERROR(dev,
432
"Unable to create hwmon sysfs file: %d\n", ret);
433
hwmon_device_unregister(hwmon_dev);
434
return ret;
435
}
436
437
pm->hwmon = hwmon_dev;
438
#endif
439
return 0;
440
}
441
442
static void
443
nouveau_hwmon_fini(struct drm_device *dev)
444
{
445
#ifdef CONFIG_HWMON
446
struct drm_nouveau_private *dev_priv = dev->dev_private;
447
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
448
449
if (pm->hwmon) {
450
sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
451
hwmon_device_unregister(pm->hwmon);
452
}
453
#endif
454
}
455
456
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
457
static int
458
nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
459
{
460
struct drm_nouveau_private *dev_priv =
461
container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
462
struct drm_device *dev = dev_priv->dev;
463
struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
464
465
if (strcmp(entry->device_class, "ac_adapter") == 0) {
466
bool ac = power_supply_is_system_supplied();
467
468
NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
469
}
470
471
return NOTIFY_OK;
472
}
473
#endif
474
475
int
476
nouveau_pm_init(struct drm_device *dev)
477
{
478
struct drm_nouveau_private *dev_priv = dev->dev_private;
479
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
480
char info[256];
481
int ret, i;
482
483
nouveau_mem_timing_init(dev);
484
nouveau_volt_init(dev);
485
nouveau_perf_init(dev);
486
nouveau_temp_init(dev);
487
488
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
489
for (i = 0; i < pm->nr_perflvl; i++) {
490
nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
491
NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info);
492
}
493
494
/* determine current ("boot") performance level */
495
ret = nouveau_pm_perflvl_get(dev, &pm->boot);
496
if (ret == 0) {
497
strncpy(pm->boot.name, "boot", 4);
498
pm->cur = &pm->boot;
499
500
nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
501
NV_INFO(dev, "c: %s", info);
502
}
503
504
/* switch performance levels now if requested */
505
if (nouveau_perflvl != NULL) {
506
ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
507
if (ret) {
508
NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
509
nouveau_perflvl, ret);
510
}
511
}
512
513
nouveau_sysfs_init(dev);
514
nouveau_hwmon_init(dev);
515
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
516
pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
517
register_acpi_notifier(&pm->acpi_nb);
518
#endif
519
520
return 0;
521
}
522
523
void
524
nouveau_pm_fini(struct drm_device *dev)
525
{
526
struct drm_nouveau_private *dev_priv = dev->dev_private;
527
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
528
529
if (pm->cur != &pm->boot)
530
nouveau_pm_perflvl_set(dev, &pm->boot);
531
532
nouveau_temp_fini(dev);
533
nouveau_perf_fini(dev);
534
nouveau_volt_fini(dev);
535
nouveau_mem_timing_fini(dev);
536
537
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
538
unregister_acpi_notifier(&pm->acpi_nb);
539
#endif
540
nouveau_hwmon_fini(dev);
541
nouveau_sysfs_fini(dev);
542
}
543
544
void
545
nouveau_pm_resume(struct drm_device *dev)
546
{
547
struct drm_nouveau_private *dev_priv = dev->dev_private;
548
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
549
struct nouveau_pm_level *perflvl;
550
551
if (!pm->cur || pm->cur == &pm->boot)
552
return;
553
554
perflvl = pm->cur;
555
pm->cur = &pm->boot;
556
nouveau_pm_perflvl_set(dev, perflvl);
557
}
558
559