blob: b94364dbe3523b28a10a61d94664692cdc1e1bf0 [file] [log] [blame]
Ben Skeggs330c5982010-09-16 15:39:49 +10001/*
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
Ben Skeggs60326492010-10-12 12:31:32 +100030#ifdef CONFIG_ACPI
31#include <linux/acpi.h>
32#endif
33#include <linux/power_supply.h>
Martin Peres34e9d852010-09-22 20:54:22 +020034#include <linux/hwmon.h>
35#include <linux/hwmon-sysfs.h>
36
Ben Skeggs330c5982010-09-16 15:39:49 +100037static int
Ben Skeggsa1750942011-09-17 01:42:12 +100038nouveau_pwmfan_get(struct drm_device *dev)
39{
40 struct drm_nouveau_private *dev_priv = dev->dev_private;
41 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
42 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
43 struct dcb_gpio_entry *gpio;
44 u32 divs, duty;
45 int ret;
46
47 if (!pm->pwm_get) {
48 if (pm->fanspeed_get)
49 return pm->fanspeed_get(dev);
50 return -ENODEV;
51 }
52
53 gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN);
54 if (gpio) {
55 ret = pm->pwm_get(dev, gpio, &divs, &duty);
56 if (ret == 0) {
57 divs = max(divs, duty);
58 if (dev_priv->card_type <= NV_40 ||
59 (gpio->state[0] & 1))
60 duty = divs - duty;
61 return (duty * 100) / divs;
62 }
63
64 return pgpio->get(dev, gpio->tag) * 100;
65 }
66
67 return -ENODEV;
68}
69
70static int
71nouveau_pwmfan_set(struct drm_device *dev, int percent)
72{
73 struct drm_nouveau_private *dev_priv = dev->dev_private;
74 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
75 struct dcb_gpio_entry *gpio;
76 u32 divs, duty;
77
78 if (!pm->pwm_set) {
79 if (pm->fanspeed_set)
80 return pm->fanspeed_set(dev, percent);
81 return -ENODEV;
82 }
83
84 gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN);
85 if (gpio) {
86 divs = pm->pwm_divisor;
87 if (pm->fan.pwm_freq) {
88 /*XXX: PNVIO clock more than likely... */
89 divs = 135000 / pm->fan.pwm_freq;
90 if (dev_priv->chipset < 0xa3)
91 divs /= 4;
92 }
93
94 duty = ((divs * percent) + 99) / 100;
95 if (dev_priv->card_type <= NV_40 ||
96 (gpio->state[0] & 1))
97 duty = divs - duty;
98
99 return pm->pwm_set(dev, gpio, divs, duty);
100 }
101
102 return -ENODEV;
103}
104
105static int
Ben Skeggs5c6dc652010-09-27 09:47:56 +1000106nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,
107 u8 id, u32 khz)
Ben Skeggs6f876982010-09-16 16:47:14 +1000108{
109 struct drm_nouveau_private *dev_priv = dev->dev_private;
110 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
111 void *pre_state;
112
113 if (khz == 0)
114 return 0;
115
Ben Skeggs5c6dc652010-09-27 09:47:56 +1000116 pre_state = pm->clock_pre(dev, perflvl, id, khz);
Ben Skeggs6f876982010-09-16 16:47:14 +1000117 if (IS_ERR(pre_state))
118 return PTR_ERR(pre_state);
119
120 if (pre_state)
121 pm->clock_set(dev, pre_state);
122 return 0;
123}
124
125static int
Ben Skeggs64f1c112010-09-17 13:35:25 +1000126nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
127{
128 struct drm_nouveau_private *dev_priv = dev->dev_private;
129 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
130 int ret;
131
132 if (perflvl == pm->cur)
133 return 0;
134
Martin Peres11b7d8952011-08-15 11:10:30 +1000135 /*XXX: not on all boards, we should control based on temperature
136 * on recent boards.. or maybe on some other factor we don't
137 * know about?
138 */
Ben Skeggsa1750942011-09-17 01:42:12 +1000139 if (perflvl->fanspeed) {
140 ret = nouveau_pwmfan_set(dev, perflvl->fanspeed);
141 if (ret && ret != -ENODEV)
Ben Skeggs771e1032011-07-28 11:01:21 +1000142 NV_ERROR(dev, "set fanspeed failed: %d\n", ret);
143 }
144
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000145 if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
146 ret = pm->voltage_set(dev, perflvl->volt_min);
Ben Skeggs64f1c112010-09-17 13:35:25 +1000147 if (ret) {
148 NV_ERROR(dev, "voltage_set %d failed: %d\n",
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000149 perflvl->volt_min, ret);
Ben Skeggs64f1c112010-09-17 13:35:25 +1000150 }
151 }
152
Ben Skeggs77e7da62011-06-17 11:25:57 +1000153 if (pm->clocks_pre) {
154 void *state = pm->clocks_pre(dev, perflvl);
155 if (IS_ERR(state))
156 return PTR_ERR(state);
157 pm->clocks_set(dev, state);
158 } else
Ben Skeggsda1dc4c2011-06-10 12:07:09 +1000159 if (pm->clock_set) {
160 nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
161 nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
162 nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
163 nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
164 }
Ben Skeggs64f1c112010-09-17 13:35:25 +1000165
166 pm->cur = perflvl;
167 return 0;
168}
169
170static int
Ben Skeggs6f876982010-09-16 16:47:14 +1000171nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
172{
173 struct drm_nouveau_private *dev_priv = dev->dev_private;
174 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
175 struct nouveau_pm_level *perflvl = NULL;
Ben Skeggs6f876982010-09-16 16:47:14 +1000176
177 /* safety precaution, for now */
178 if (nouveau_perflvl_wr != 7777)
179 return -EPERM;
180
Ben Skeggs6f876982010-09-16 16:47:14 +1000181 if (!strncmp(profile, "boot", 4))
182 perflvl = &pm->boot;
183 else {
184 int pl = simple_strtol(profile, NULL, 10);
185 int i;
186
187 for (i = 0; i < pm->nr_perflvl; i++) {
188 if (pm->perflvl[i].id == pl) {
189 perflvl = &pm->perflvl[i];
190 break;
191 }
192 }
193
194 if (!perflvl)
195 return -EINVAL;
196 }
197
Ben Skeggs6f876982010-09-16 16:47:14 +1000198 NV_INFO(dev, "setting performance level: %s\n", profile);
Ben Skeggs64f1c112010-09-17 13:35:25 +1000199 return nouveau_pm_perflvl_set(dev, perflvl);
Ben Skeggs6f876982010-09-16 16:47:14 +1000200}
201
202static int
Ben Skeggs330c5982010-09-16 15:39:49 +1000203nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
204{
205 struct drm_nouveau_private *dev_priv = dev->dev_private;
206 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
207 int ret;
208
Ben Skeggs330c5982010-09-16 15:39:49 +1000209 memset(perflvl, 0, sizeof(*perflvl));
210
Ben Skeggs77e7da62011-06-17 11:25:57 +1000211 if (pm->clocks_get) {
212 ret = pm->clocks_get(dev, perflvl);
213 if (ret)
214 return ret;
215 } else
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000216 if (pm->clock_get) {
217 ret = pm->clock_get(dev, PLL_CORE);
218 if (ret > 0)
219 perflvl->core = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000220
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000221 ret = pm->clock_get(dev, PLL_MEMORY);
222 if (ret > 0)
223 perflvl->memory = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000224
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000225 ret = pm->clock_get(dev, PLL_SHADER);
226 if (ret > 0)
227 perflvl->shader = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000228
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000229 ret = pm->clock_get(dev, PLL_UNK05);
230 if (ret > 0)
231 perflvl->unk05 = ret;
232 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000233
234 if (pm->voltage.supported && pm->voltage_get) {
235 ret = pm->voltage_get(dev);
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000236 if (ret > 0) {
237 perflvl->volt_min = ret;
238 perflvl->volt_max = ret;
239 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000240 }
241
Ben Skeggsa1750942011-09-17 01:42:12 +1000242 ret = nouveau_pwmfan_get(dev);
243 if (ret > 0)
244 perflvl->fanspeed = ret;
Ben Skeggs771e1032011-07-28 11:01:21 +1000245
Ben Skeggs330c5982010-09-16 15:39:49 +1000246 return 0;
247}
248
249static void
250nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
251{
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000252 char c[16], s[16], v[32], f[16], t[16], m[16];
Francisco Jerez0fbb1142010-09-20 16:18:28 +0200253
254 c[0] = '\0';
255 if (perflvl->core)
256 snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
Ben Skeggs330c5982010-09-16 15:39:49 +1000257
258 s[0] = '\0';
259 if (perflvl->shader)
260 snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
261
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000262 m[0] = '\0';
263 if (perflvl->memory)
264 snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
265
Ben Skeggs330c5982010-09-16 15:39:49 +1000266 v[0] = '\0';
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000267 if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
268 snprintf(v, sizeof(v), " voltage %dmV-%dmV",
269 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
270 } else
271 if (perflvl->volt_min) {
272 snprintf(v, sizeof(v), " voltage %dmV",
273 perflvl->volt_min / 1000);
274 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000275
276 f[0] = '\0';
277 if (perflvl->fanspeed)
278 snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
279
Martin Perese614b2e2011-04-14 00:46:19 +0200280 t[0] = '\0';
281 if (perflvl->timing)
282 snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
283
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000284 snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
Ben Skeggs330c5982010-09-16 15:39:49 +1000285}
286
287static ssize_t
288nouveau_pm_get_perflvl_info(struct device *d,
289 struct device_attribute *a, char *buf)
290{
291 struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
292 char *ptr = buf;
293 int len = PAGE_SIZE;
294
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000295 snprintf(ptr, len, "%d:", perflvl->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000296 ptr += strlen(buf);
297 len -= strlen(buf);
298
299 nouveau_pm_perflvl_info(perflvl, ptr, len);
300 return strlen(buf);
301}
302
303static ssize_t
304nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
305{
Martin Peres34e9d852010-09-22 20:54:22 +0200306 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs330c5982010-09-16 15:39:49 +1000307 struct drm_nouveau_private *dev_priv = dev->dev_private;
308 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
309 struct nouveau_pm_level cur;
310 int len = PAGE_SIZE, ret;
311 char *ptr = buf;
312
313 if (!pm->cur)
314 snprintf(ptr, len, "setting: boot\n");
315 else if (pm->cur == &pm->boot)
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000316 snprintf(ptr, len, "setting: boot\nc:");
Ben Skeggs330c5982010-09-16 15:39:49 +1000317 else
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000318 snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000319 ptr += strlen(buf);
320 len -= strlen(buf);
321
322 ret = nouveau_pm_perflvl_get(dev, &cur);
323 if (ret == 0)
324 nouveau_pm_perflvl_info(&cur, ptr, len);
325 return strlen(buf);
326}
327
328static ssize_t
329nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
330 const char *buf, size_t count)
331{
Martin Peres34e9d852010-09-22 20:54:22 +0200332 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs6f876982010-09-16 16:47:14 +1000333 int ret;
334
335 ret = nouveau_pm_profile_set(dev, buf);
336 if (ret)
337 return ret;
338 return strlen(buf);
Ben Skeggs330c5982010-09-16 15:39:49 +1000339}
340
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200341static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
342 nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
Ben Skeggs330c5982010-09-16 15:39:49 +1000343
Martin Peres34e9d852010-09-22 20:54:22 +0200344static int
345nouveau_sysfs_init(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000346{
347 struct drm_nouveau_private *dev_priv = dev->dev_private;
348 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
349 struct device *d = &dev->pdev->dev;
Ben Skeggs330c5982010-09-16 15:39:49 +1000350 int ret, i;
351
Ben Skeggs330c5982010-09-16 15:39:49 +1000352 ret = device_create_file(d, &dev_attr_performance_level);
353 if (ret)
354 return ret;
355
356 for (i = 0; i < pm->nr_perflvl; i++) {
357 struct nouveau_pm_level *perflvl = &pm->perflvl[i];
358
359 perflvl->dev_attr.attr.name = perflvl->name;
360 perflvl->dev_attr.attr.mode = S_IRUGO;
361 perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
362 perflvl->dev_attr.store = NULL;
363 sysfs_attr_init(&perflvl->dev_attr.attr);
364
365 ret = device_create_file(d, &perflvl->dev_attr);
366 if (ret) {
367 NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
368 perflvl->id, i);
369 perflvl->dev_attr.attr.name = NULL;
370 nouveau_pm_fini(dev);
371 return ret;
372 }
373 }
374
375 return 0;
376}
377
Martin Peres34e9d852010-09-22 20:54:22 +0200378static void
379nouveau_sysfs_fini(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000380{
381 struct drm_nouveau_private *dev_priv = dev->dev_private;
382 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
383 struct device *d = &dev->pdev->dev;
384 int i;
385
386 device_remove_file(d, &dev_attr_performance_level);
387 for (i = 0; i < pm->nr_perflvl; i++) {
388 struct nouveau_pm_level *pl = &pm->perflvl[i];
389
390 if (!pl->dev_attr.attr.name)
391 break;
392
393 device_remove_file(d, &pl->dev_attr);
394 }
Martin Peres34e9d852010-09-22 20:54:22 +0200395}
396
Ken Milmore658e86e2011-07-03 19:54:28 +0100397#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200398static ssize_t
399nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
400{
401 struct drm_device *dev = dev_get_drvdata(d);
Francisco Jerez8155cac2010-09-23 20:58:38 +0200402 struct drm_nouveau_private *dev_priv = dev->dev_private;
403 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200404
Francisco Jerez8155cac2010-09-23 20:58:38 +0200405 return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200406}
407static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
408 NULL, 0);
409
410static ssize_t
411nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
412{
413 struct drm_device *dev = dev_get_drvdata(d);
414 struct drm_nouveau_private *dev_priv = dev->dev_private;
415 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
416 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
417
418 return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
419}
420static ssize_t
421nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
422 const char *buf, size_t count)
423{
424 struct drm_device *dev = dev_get_drvdata(d);
425 struct drm_nouveau_private *dev_priv = dev->dev_private;
426 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
427 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
428 long value;
429
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200430 if (strict_strtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200431 return count;
432
433 temp->down_clock = value/1000;
434
435 nouveau_temp_safety_checks(dev);
436
437 return count;
438}
439static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
440 nouveau_hwmon_set_max_temp,
441 0);
442
443static ssize_t
444nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
445 char *buf)
446{
447 struct drm_device *dev = dev_get_drvdata(d);
448 struct drm_nouveau_private *dev_priv = dev->dev_private;
449 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
450 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
451
452 return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
453}
454static ssize_t
455nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
456 const char *buf,
457 size_t count)
458{
459 struct drm_device *dev = dev_get_drvdata(d);
460 struct drm_nouveau_private *dev_priv = dev->dev_private;
461 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
462 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
463 long value;
464
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200465 if (strict_strtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200466 return count;
467
468 temp->critical = value/1000;
469
470 nouveau_temp_safety_checks(dev);
471
472 return count;
473}
474static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
475 nouveau_hwmon_critical_temp,
476 nouveau_hwmon_set_critical_temp,
477 0);
478
479static ssize_t nouveau_hwmon_show_name(struct device *dev,
480 struct device_attribute *attr,
481 char *buf)
482{
483 return sprintf(buf, "nouveau\n");
484}
485static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
486
487static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
488 struct device_attribute *attr,
489 char *buf)
490{
491 return sprintf(buf, "1000\n");
492}
493static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
494 nouveau_hwmon_show_update_rate,
495 NULL, 0);
496
Martin Peres11b7d8952011-08-15 11:10:30 +1000497static ssize_t
498nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
499 char *buf)
500{
501 struct drm_device *dev = dev_get_drvdata(d);
502 struct drm_nouveau_private *dev_priv = dev->dev_private;
503 struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
504 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
505 struct dcb_gpio_entry *gpio;
506 u32 cycles, cur, prev;
507 u64 start;
508
509 gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE);
510 if (!gpio)
511 return -ENODEV;
512
513 /* Monitor the GPIO input 0x3b for 250ms.
514 * When the fan spins, it changes the value of GPIO FAN_SENSE.
515 * We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation.
516 */
517 start = ptimer->read(dev);
518 prev = pgpio->get(dev, DCB_GPIO_FAN_SENSE);
519 cycles = 0;
520 do {
521 cur = pgpio->get(dev, DCB_GPIO_FAN_SENSE);
522 if (prev != cur) {
523 cycles++;
524 prev = cur;
525 }
526
527 usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
528 } while (ptimer->read(dev) - start < 250000000);
529
530 /* interpolate to get rpm */
531 return sprintf(buf, "%i\n", cycles / 4 * 4 * 60);
532}
533static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input,
534 NULL, 0);
535
536static ssize_t
537nouveau_hwmon_get_pwm0(struct device *d, struct device_attribute *a, char *buf)
538{
539 struct drm_device *dev = dev_get_drvdata(d);
Ben Skeggsa1750942011-09-17 01:42:12 +1000540 int ret;
Martin Peres11b7d8952011-08-15 11:10:30 +1000541
Ben Skeggsa1750942011-09-17 01:42:12 +1000542 ret = nouveau_pwmfan_get(dev);
Martin Peres11b7d8952011-08-15 11:10:30 +1000543 if (ret < 0)
544 return ret;
545
546 return sprintf(buf, "%i\n", ret);
547}
548
549static ssize_t
550nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a,
551 const char *buf, size_t count)
552{
553 struct drm_device *dev = dev_get_drvdata(d);
554 struct drm_nouveau_private *dev_priv = dev->dev_private;
555 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
556 int ret = -ENODEV;
557 long value;
558
559 if (nouveau_perflvl_wr != 7777)
560 return -EPERM;
561
562 if (strict_strtol(buf, 10, &value) == -EINVAL)
563 return -EINVAL;
564
565 if (value < pm->fan.min_duty)
566 value = pm->fan.min_duty;
567 if (value > pm->fan.max_duty)
568 value = pm->fan.max_duty;
569
Ben Skeggsa1750942011-09-17 01:42:12 +1000570 ret = nouveau_pwmfan_set(dev, value);
Martin Peres11b7d8952011-08-15 11:10:30 +1000571 if (ret)
572 return ret;
573
574 return count;
575}
576
577static SENSOR_DEVICE_ATTR(pwm0, S_IRUGO | S_IWUSR,
578 nouveau_hwmon_get_pwm0,
579 nouveau_hwmon_set_pwm0, 0);
580
581static ssize_t
582nouveau_hwmon_get_pwm0_min(struct device *d,
583 struct device_attribute *a, char *buf)
584{
585 struct drm_device *dev = dev_get_drvdata(d);
586 struct drm_nouveau_private *dev_priv = dev->dev_private;
587 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
588
589 return sprintf(buf, "%i\n", pm->fan.min_duty);
590}
591
592static ssize_t
593nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a,
594 const char *buf, size_t count)
595{
596 struct drm_device *dev = dev_get_drvdata(d);
597 struct drm_nouveau_private *dev_priv = dev->dev_private;
598 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
599 long value;
600
601 if (strict_strtol(buf, 10, &value) == -EINVAL)
602 return -EINVAL;
603
604 if (value < 0)
605 value = 0;
606
607 if (pm->fan.max_duty - value < 10)
608 value = pm->fan.max_duty - 10;
609
610 if (value < 10)
611 pm->fan.min_duty = 10;
612 else
613 pm->fan.min_duty = value;
614
615 return count;
616}
617
618static SENSOR_DEVICE_ATTR(pwm0_min, S_IRUGO | S_IWUSR,
619 nouveau_hwmon_get_pwm0_min,
620 nouveau_hwmon_set_pwm0_min, 0);
621
622static ssize_t
623nouveau_hwmon_get_pwm0_max(struct device *d,
624 struct device_attribute *a, char *buf)
625{
626 struct drm_device *dev = dev_get_drvdata(d);
627 struct drm_nouveau_private *dev_priv = dev->dev_private;
628 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
629
630 return sprintf(buf, "%i\n", pm->fan.max_duty);
631}
632
633static ssize_t
634nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a,
635 const char *buf, size_t count)
636{
637 struct drm_device *dev = dev_get_drvdata(d);
638 struct drm_nouveau_private *dev_priv = dev->dev_private;
639 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
640 long value;
641
642 if (strict_strtol(buf, 10, &value) == -EINVAL)
643 return -EINVAL;
644
645 if (value < 0)
646 value = 0;
647
648 if (value - pm->fan.min_duty < 10)
649 value = pm->fan.min_duty + 10;
650
651 if (value > 100)
652 pm->fan.max_duty = 100;
653 else
654 pm->fan.max_duty = value;
655
656 return count;
657}
658
659static SENSOR_DEVICE_ATTR(pwm0_max, S_IRUGO | S_IWUSR,
660 nouveau_hwmon_get_pwm0_max,
661 nouveau_hwmon_set_pwm0_max, 0);
662
Martin Peres34e9d852010-09-22 20:54:22 +0200663static struct attribute *hwmon_attributes[] = {
664 &sensor_dev_attr_temp1_input.dev_attr.attr,
665 &sensor_dev_attr_temp1_max.dev_attr.attr,
666 &sensor_dev_attr_temp1_crit.dev_attr.attr,
667 &sensor_dev_attr_name.dev_attr.attr,
668 &sensor_dev_attr_update_rate.dev_attr.attr,
669 NULL
670};
Martin Peres11b7d8952011-08-15 11:10:30 +1000671static struct attribute *hwmon_fan_rpm_attributes[] = {
672 &sensor_dev_attr_fan0_input.dev_attr.attr,
673 NULL
674};
675static struct attribute *hwmon_pwm_fan_attributes[] = {
676 &sensor_dev_attr_pwm0.dev_attr.attr,
677 &sensor_dev_attr_pwm0_min.dev_attr.attr,
678 &sensor_dev_attr_pwm0_max.dev_attr.attr,
679 NULL
680};
Martin Peres34e9d852010-09-22 20:54:22 +0200681
682static const struct attribute_group hwmon_attrgroup = {
683 .attrs = hwmon_attributes,
684};
Martin Peres11b7d8952011-08-15 11:10:30 +1000685static const struct attribute_group hwmon_fan_rpm_attrgroup = {
686 .attrs = hwmon_fan_rpm_attributes,
687};
688static const struct attribute_group hwmon_pwm_fan_attrgroup = {
689 .attrs = hwmon_pwm_fan_attributes,
690};
Martin Peresb54262f2010-10-26 12:48:28 +0200691#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200692
693static int
694nouveau_hwmon_init(struct drm_device *dev)
695{
Ken Milmore658e86e2011-07-03 19:54:28 +0100696#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200697 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez8155cac2010-09-23 20:58:38 +0200698 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200699 struct device *hwmon_dev;
Martin Peres11b7d8952011-08-15 11:10:30 +1000700 int ret = 0;
Martin Peres34e9d852010-09-22 20:54:22 +0200701
Francisco Jerez8155cac2010-09-23 20:58:38 +0200702 if (!pm->temp_get)
703 return -ENODEV;
Martin Peres34e9d852010-09-22 20:54:22 +0200704
705 hwmon_dev = hwmon_device_register(&dev->pdev->dev);
706 if (IS_ERR(hwmon_dev)) {
707 ret = PTR_ERR(hwmon_dev);
708 NV_ERROR(dev,
709 "Unable to register hwmon device: %d\n", ret);
710 return ret;
711 }
712 dev_set_drvdata(hwmon_dev, dev);
Martin Peres11b7d8952011-08-15 11:10:30 +1000713
714 /* default sysfs entries */
Lucas Stach07cfe0e2011-01-06 20:29:53 +0100715 ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Martin Peres34e9d852010-09-22 20:54:22 +0200716 if (ret) {
Martin Peres11b7d8952011-08-15 11:10:30 +1000717 if (ret)
718 goto error;
719 }
720
721 /* if the card has a pwm fan */
722 /*XXX: incorrect, need better detection for this, some boards have
723 * the gpio entries for pwm fan control even when there's no
724 * actual fan connected to it... therm table? */
Ben Skeggsa1750942011-09-17 01:42:12 +1000725 if (nouveau_pwmfan_get(dev) >= 0) {
Martin Peres11b7d8952011-08-15 11:10:30 +1000726 ret = sysfs_create_group(&dev->pdev->dev.kobj,
727 &hwmon_pwm_fan_attrgroup);
728 if (ret)
729 goto error;
730 }
731
732 /* if the card can read the fan rpm */
733 if (nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE)) {
734 ret = sysfs_create_group(&dev->pdev->dev.kobj,
735 &hwmon_fan_rpm_attrgroup);
736 if (ret)
737 goto error;
Martin Peres34e9d852010-09-22 20:54:22 +0200738 }
739
Francisco Jerez8155cac2010-09-23 20:58:38 +0200740 pm->hwmon = hwmon_dev;
Martin Peres11b7d8952011-08-15 11:10:30 +1000741
Martin Peres34e9d852010-09-22 20:54:22 +0200742 return 0;
Martin Peres11b7d8952011-08-15 11:10:30 +1000743
744error:
745 NV_ERROR(dev, "Unable to create some hwmon sysfs files: %d\n", ret);
746 hwmon_device_unregister(hwmon_dev);
747 pm->hwmon = NULL;
748 return ret;
749#else
750 pm->hwmon = NULL;
751 return 0;
752#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200753}
754
755static void
756nouveau_hwmon_fini(struct drm_device *dev)
757{
Ken Milmore658e86e2011-07-03 19:54:28 +0100758#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200759 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez8155cac2010-09-23 20:58:38 +0200760 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200761
Francisco Jerez8155cac2010-09-23 20:58:38 +0200762 if (pm->hwmon) {
Lucas Stach8c06a3e2011-01-30 10:54:11 +0100763 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Martin Peres11b7d8952011-08-15 11:10:30 +1000764 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup);
765 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup);
766
Francisco Jerez8155cac2010-09-23 20:58:38 +0200767 hwmon_device_unregister(pm->hwmon);
Martin Peres34e9d852010-09-22 20:54:22 +0200768 }
Martin Peresb54262f2010-10-26 12:48:28 +0200769#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200770}
771
Martin Peres1f962792011-04-12 00:55:44 +0200772#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000773static int
774nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
775{
776 struct drm_nouveau_private *dev_priv =
777 container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
778 struct drm_device *dev = dev_priv->dev;
779 struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
780
781 if (strcmp(entry->device_class, "ac_adapter") == 0) {
782 bool ac = power_supply_is_system_supplied();
783
784 NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
785 }
786
787 return NOTIFY_OK;
788}
789#endif
790
Martin Peres34e9d852010-09-22 20:54:22 +0200791int
792nouveau_pm_init(struct drm_device *dev)
793{
794 struct drm_nouveau_private *dev_priv = dev->dev_private;
795 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
796 char info[256];
797 int ret, i;
798
Martin Perese614b2e2011-04-14 00:46:19 +0200799 nouveau_mem_timing_init(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200800 nouveau_volt_init(dev);
801 nouveau_perf_init(dev);
802 nouveau_temp_init(dev);
803
804 NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
805 for (i = 0; i < pm->nr_perflvl; i++) {
806 nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000807 NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
Martin Peres34e9d852010-09-22 20:54:22 +0200808 }
809
810 /* determine current ("boot") performance level */
811 ret = nouveau_pm_perflvl_get(dev, &pm->boot);
812 if (ret == 0) {
Martin Peres01e542c2011-03-19 22:44:35 +0100813 strncpy(pm->boot.name, "boot", 4);
Martin Peres34e9d852010-09-22 20:54:22 +0200814 pm->cur = &pm->boot;
815
816 nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000817 NV_INFO(dev, "c:%s", info);
Martin Peres34e9d852010-09-22 20:54:22 +0200818 }
819
820 /* switch performance levels now if requested */
821 if (nouveau_perflvl != NULL) {
822 ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
823 if (ret) {
824 NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
825 nouveau_perflvl, ret);
826 }
827 }
828
829 nouveau_sysfs_init(dev);
830 nouveau_hwmon_init(dev);
Martin Peres1f962792011-04-12 00:55:44 +0200831#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000832 pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
833 register_acpi_notifier(&pm->acpi_nb);
834#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200835
836 return 0;
837}
838
839void
840nouveau_pm_fini(struct drm_device *dev)
841{
842 struct drm_nouveau_private *dev_priv = dev->dev_private;
843 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
844
845 if (pm->cur != &pm->boot)
846 nouveau_pm_perflvl_set(dev, &pm->boot);
Ben Skeggs330c5982010-09-16 15:39:49 +1000847
Roy Spliet7760fcb2010-09-17 23:17:24 +0200848 nouveau_temp_fini(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000849 nouveau_perf_fini(dev);
850 nouveau_volt_fini(dev);
Martin Perese614b2e2011-04-14 00:46:19 +0200851 nouveau_mem_timing_fini(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200852
Martin Peres1f962792011-04-12 00:55:44 +0200853#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000854 unregister_acpi_notifier(&pm->acpi_nb);
855#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200856 nouveau_hwmon_fini(dev);
857 nouveau_sysfs_fini(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000858}
859
Ben Skeggs64f1c112010-09-17 13:35:25 +1000860void
861nouveau_pm_resume(struct drm_device *dev)
862{
863 struct drm_nouveau_private *dev_priv = dev->dev_private;
864 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
865 struct nouveau_pm_level *perflvl;
866
Ben Skeggs317495b2011-02-17 11:11:28 +1000867 if (!pm->cur || pm->cur == &pm->boot)
Ben Skeggs64f1c112010-09-17 13:35:25 +1000868 return;
869
870 perflvl = pm->cur;
871 pm->cur = &pm->boot;
872 nouveau_pm_perflvl_set(dev, perflvl);
873}