blob: 3a6ee7e48753b33c4f8096126ede0e6e8a9b2b8b [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
Ben Skeggs60326492010-10-12 12:31:32 +100025#ifdef CONFIG_ACPI
26#include <linux/acpi.h>
27#endif
28#include <linux/power_supply.h>
Martin Peres34e9d852010-09-22 20:54:22 +020029#include <linux/hwmon.h>
30#include <linux/hwmon-sysfs.h>
31
Linus Torvalds612a9aa2012-10-03 23:29:23 -070032#include <drm/drmP.h>
Ben Skeggsa1750942011-09-17 01:42:12 +100033
Ben Skeggs77145f12012-07-31 16:16:21 +100034#include "nouveau_drm.h"
35#include "nouveau_pm.h"
Ben Skeggsa1750942011-09-17 01:42:12 +100036
Ben Skeggs77145f12012-07-31 16:16:21 +100037#include <subdev/gpio.h>
38#include <subdev/timer.h>
Martin Peresaa1b9b42012-09-02 02:55:58 +020039#include <subdev/therm.h>
Ben Skeggsa1750942011-09-17 01:42:12 +100040
Ben Skeggs77145f12012-07-31 16:16:21 +100041MODULE_PARM_DESC(perflvl, "Performance level (default: boot)");
42static char *nouveau_perflvl;
43module_param_named(perflvl, nouveau_perflvl, charp, 0400);
Ben Skeggsa1750942011-09-17 01:42:12 +100044
Ben Skeggs77145f12012-07-31 16:16:21 +100045MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)");
46static int nouveau_perflvl_wr;
47module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400);
Ben Skeggsa1750942011-09-17 01:42:12 +100048
49static int
Ben Skeggs0b627a02011-10-27 12:02:12 +100050nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl,
51 struct nouveau_pm_level *a, struct nouveau_pm_level *b)
52{
Ben Skeggs77145f12012-07-31 16:16:21 +100053 struct nouveau_drm *drm = nouveau_drm(dev);
54 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peresa6fd5cf2012-10-04 00:44:19 +020055 struct nouveau_therm *therm = nouveau_therm(drm->device);
Ben Skeggs0b627a02011-10-27 12:02:12 +100056 int ret;
57
58 /*XXX: not on all boards, we should control based on temperature
59 * on recent boards.. or maybe on some other factor we don't
60 * know about?
61 */
Martin Peresaa1b9b42012-09-02 02:55:58 +020062 if (therm && therm->fan_set &&
63 a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) {
64 ret = therm->fan_set(therm, perflvl->fanspeed);
Ben Skeggs0b627a02011-10-27 12:02:12 +100065 if (ret && ret != -ENODEV) {
Ben Skeggs77145f12012-07-31 16:16:21 +100066 NV_ERROR(drm, "fanspeed set failed: %d\n", ret);
Ben Skeggs0b627a02011-10-27 12:02:12 +100067 }
68 }
69
70 if (pm->voltage.supported && pm->voltage_set) {
Ben Skeggsd2edab42011-11-10 13:20:14 +100071 if (perflvl->volt_min && b->volt_min > a->volt_min) {
Ben Skeggs0b627a02011-10-27 12:02:12 +100072 ret = pm->voltage_set(dev, perflvl->volt_min);
73 if (ret) {
Ben Skeggs77145f12012-07-31 16:16:21 +100074 NV_ERROR(drm, "voltage set failed: %d\n", ret);
Ben Skeggs0b627a02011-10-27 12:02:12 +100075 return ret;
76 }
77 }
78 }
79
80 return 0;
81}
82
83static int
Ben Skeggs64f1c112010-09-17 13:35:25 +100084nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
85{
Ben Skeggs77145f12012-07-31 16:16:21 +100086 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggsff2b6c62011-10-27 10:28:17 +100087 void *state;
Ben Skeggs64f1c112010-09-17 13:35:25 +100088 int ret;
89
90 if (perflvl == pm->cur)
91 return 0;
92
Ben Skeggs0b627a02011-10-27 12:02:12 +100093 ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl);
94 if (ret)
95 return ret;
Ben Skeggs64f1c112010-09-17 13:35:25 +100096
Ben Skeggsff2b6c62011-10-27 10:28:17 +100097 state = pm->clocks_pre(dev, perflvl);
Martin Peresb0103742011-11-03 00:03:06 +010098 if (IS_ERR(state)) {
99 ret = PTR_ERR(state);
100 goto error;
101 }
102 ret = pm->clocks_set(dev, state);
103 if (ret)
104 goto error;
Ben Skeggs64f1c112010-09-17 13:35:25 +1000105
Ben Skeggs0b627a02011-10-27 12:02:12 +1000106 ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
107 if (ret)
108 return ret;
109
Ben Skeggs64f1c112010-09-17 13:35:25 +1000110 pm->cur = perflvl;
111 return 0;
Martin Peresb0103742011-11-03 00:03:06 +0100112
113error:
114 /* restore the fan speed and voltage before leaving */
115 nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
116 return ret;
Ben Skeggs64f1c112010-09-17 13:35:25 +1000117}
118
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000119void
120nouveau_pm_trigger(struct drm_device *dev)
121{
Ben Skeggs77145f12012-07-31 16:16:21 +1000122 struct nouveau_drm *drm = nouveau_drm(dev);
123 struct nouveau_timer *ptimer = nouveau_timer(drm->device);
124 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000125 struct nouveau_pm_profile *profile = NULL;
126 struct nouveau_pm_level *perflvl = NULL;
127 int ret;
128
129 /* select power profile based on current power source */
130 if (power_supply_is_system_supplied())
131 profile = pm->profile_ac;
132 else
133 profile = pm->profile_dc;
134
Ben Skeggs25c53c12012-01-24 18:03:25 +1000135 if (profile != pm->profile) {
136 pm->profile->func->fini(pm->profile);
137 pm->profile = profile;
138 pm->profile->func->init(pm->profile);
139 }
140
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000141 /* select performance level based on profile */
142 perflvl = profile->func->select(profile);
143
144 /* change perflvl, if necessary */
145 if (perflvl != pm->cur) {
Ben Skeggs77145f12012-07-31 16:16:21 +1000146 u64 time0 = ptimer->read(ptimer);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000147
Ben Skeggs77145f12012-07-31 16:16:21 +1000148 NV_INFO(drm, "setting performance level: %d", perflvl->id);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000149 ret = nouveau_pm_perflvl_set(dev, perflvl);
150 if (ret)
Ben Skeggs77145f12012-07-31 16:16:21 +1000151 NV_INFO(drm, "> reclocking failed: %d\n\n", ret);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000152
Ben Skeggs77145f12012-07-31 16:16:21 +1000153 NV_INFO(drm, "> reclocking took %lluns\n\n",
154 ptimer->read(ptimer) - time0);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000155 }
156}
157
158static struct nouveau_pm_profile *
159profile_find(struct drm_device *dev, const char *string)
160{
Ben Skeggs77145f12012-07-31 16:16:21 +1000161 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000162 struct nouveau_pm_profile *profile;
163
164 list_for_each_entry(profile, &pm->profiles, head) {
165 if (!strncmp(profile->name, string, sizeof(profile->name)))
166 return profile;
167 }
168
169 return NULL;
170}
171
Ben Skeggs64f1c112010-09-17 13:35:25 +1000172static int
Ben Skeggs6f876982010-09-16 16:47:14 +1000173nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
174{
Ben Skeggs77145f12012-07-31 16:16:21 +1000175 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000176 struct nouveau_pm_profile *ac = NULL, *dc = NULL;
177 char string[16], *cur = string, *ptr;
Ben Skeggs6f876982010-09-16 16:47:14 +1000178
179 /* safety precaution, for now */
180 if (nouveau_perflvl_wr != 7777)
181 return -EPERM;
182
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000183 strncpy(string, profile, sizeof(string));
Jim Meyering5799d9e2012-04-17 21:27:54 +0200184 string[sizeof(string) - 1] = 0;
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000185 if ((ptr = strchr(string, '\n')))
186 *ptr = '\0';
Ben Skeggs6f876982010-09-16 16:47:14 +1000187
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000188 ptr = strsep(&cur, ",");
189 if (ptr)
190 ac = profile_find(dev, ptr);
Ben Skeggs6f876982010-09-16 16:47:14 +1000191
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000192 ptr = strsep(&cur, ",");
193 if (ptr)
194 dc = profile_find(dev, ptr);
195 else
196 dc = ac;
Ben Skeggs6f876982010-09-16 16:47:14 +1000197
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000198 if (ac == NULL || dc == NULL)
199 return -EINVAL;
Martin Peresb0103742011-11-03 00:03:06 +0100200
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000201 pm->profile_ac = ac;
202 pm->profile_dc = dc;
203 nouveau_pm_trigger(dev);
204 return 0;
Ben Skeggs6f876982010-09-16 16:47:14 +1000205}
206
Ben Skeggs25c53c12012-01-24 18:03:25 +1000207static void
208nouveau_pm_static_dummy(struct nouveau_pm_profile *profile)
209{
210}
211
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000212static struct nouveau_pm_level *
213nouveau_pm_static_select(struct nouveau_pm_profile *profile)
214{
215 return container_of(profile, struct nouveau_pm_level, profile);
216}
217
218const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {
Ben Skeggs25c53c12012-01-24 18:03:25 +1000219 .destroy = nouveau_pm_static_dummy,
220 .init = nouveau_pm_static_dummy,
221 .fini = nouveau_pm_static_dummy,
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000222 .select = nouveau_pm_static_select,
223};
224
Ben Skeggs6f876982010-09-16 16:47:14 +1000225static int
Ben Skeggs330c5982010-09-16 15:39:49 +1000226nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
227{
Martin Peresaa1b9b42012-09-02 02:55:58 +0200228 struct nouveau_drm *drm = nouveau_drm(dev);
Ben Skeggs77145f12012-07-31 16:16:21 +1000229 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200230 struct nouveau_therm *therm = nouveau_therm(drm->device);
Ben Skeggs330c5982010-09-16 15:39:49 +1000231 int ret;
232
Ben Skeggs330c5982010-09-16 15:39:49 +1000233 memset(perflvl, 0, sizeof(*perflvl));
234
Ben Skeggsc11dd0d2012-03-07 14:18:49 +1000235 if (pm->clocks_get) {
236 ret = pm->clocks_get(dev, perflvl);
237 if (ret)
238 return ret;
239 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000240
241 if (pm->voltage.supported && pm->voltage_get) {
242 ret = pm->voltage_get(dev);
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000243 if (ret > 0) {
244 perflvl->volt_min = ret;
245 perflvl->volt_max = ret;
246 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000247 }
248
Martin Peresaa1b9b42012-09-02 02:55:58 +0200249 if (therm && therm->fan_get) {
250 ret = therm->fan_get(therm);
251 if (ret >= 0)
252 perflvl->fanspeed = ret;
253 }
Ben Skeggs771e1032011-07-28 11:01:21 +1000254
Ben Skeggsfd99fd62012-01-17 21:10:58 +1000255 nouveau_mem_timing_read(dev, &perflvl->timing);
Ben Skeggs330c5982010-09-16 15:39:49 +1000256 return 0;
257}
258
259static void
260nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
261{
Ben Skeggs085028c2012-01-18 09:02:28 +1000262 char c[16], s[16], v[32], f[16], m[16];
Francisco Jerez0fbb1142010-09-20 16:18:28 +0200263
264 c[0] = '\0';
265 if (perflvl->core)
266 snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
Ben Skeggs330c5982010-09-16 15:39:49 +1000267
268 s[0] = '\0';
269 if (perflvl->shader)
270 snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
271
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000272 m[0] = '\0';
273 if (perflvl->memory)
274 snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
275
Ben Skeggs330c5982010-09-16 15:39:49 +1000276 v[0] = '\0';
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000277 if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
278 snprintf(v, sizeof(v), " voltage %dmV-%dmV",
279 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
280 } else
281 if (perflvl->volt_min) {
282 snprintf(v, sizeof(v), " voltage %dmV",
283 perflvl->volt_min / 1000);
284 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000285
286 f[0] = '\0';
287 if (perflvl->fanspeed)
288 snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
289
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000290 snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f);
Ben Skeggs330c5982010-09-16 15:39:49 +1000291}
292
293static ssize_t
294nouveau_pm_get_perflvl_info(struct device *d,
295 struct device_attribute *a, char *buf)
296{
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000297 struct nouveau_pm_level *perflvl =
298 container_of(a, struct nouveau_pm_level, dev_attr);
Ben Skeggs330c5982010-09-16 15:39:49 +1000299 char *ptr = buf;
300 int len = PAGE_SIZE;
301
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000302 snprintf(ptr, len, "%d:", perflvl->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000303 ptr += strlen(buf);
304 len -= strlen(buf);
305
306 nouveau_pm_perflvl_info(perflvl, ptr, len);
307 return strlen(buf);
308}
309
310static ssize_t
311nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
312{
Martin Peres34e9d852010-09-22 20:54:22 +0200313 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs77145f12012-07-31 16:16:21 +1000314 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000315 struct nouveau_pm_level cur;
316 int len = PAGE_SIZE, ret;
317 char *ptr = buf;
318
Ben Skeggs8d7bb402012-01-24 15:59:07 +1000319 snprintf(ptr, len, "profile: %s, %s\nc:",
320 pm->profile_ac->name, pm->profile_dc->name);
Ben Skeggs330c5982010-09-16 15:39:49 +1000321 ptr += strlen(buf);
322 len -= strlen(buf);
323
324 ret = nouveau_pm_perflvl_get(dev, &cur);
325 if (ret == 0)
326 nouveau_pm_perflvl_info(&cur, ptr, len);
327 return strlen(buf);
328}
329
330static ssize_t
331nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
332 const char *buf, size_t count)
333{
Martin Peres34e9d852010-09-22 20:54:22 +0200334 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs6f876982010-09-16 16:47:14 +1000335 int ret;
336
337 ret = nouveau_pm_profile_set(dev, buf);
338 if (ret)
339 return ret;
340 return strlen(buf);
Ben Skeggs330c5982010-09-16 15:39:49 +1000341}
342
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200343static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
344 nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
Ben Skeggs330c5982010-09-16 15:39:49 +1000345
Martin Peres34e9d852010-09-22 20:54:22 +0200346static int
347nouveau_sysfs_init(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000348{
Ben Skeggs77145f12012-07-31 16:16:21 +1000349 struct nouveau_drm *drm = nouveau_drm(dev);
350 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000351 struct device *d = &dev->pdev->dev;
Ben Skeggs330c5982010-09-16 15:39:49 +1000352 int ret, i;
353
Ben Skeggs330c5982010-09-16 15:39:49 +1000354 ret = device_create_file(d, &dev_attr_performance_level);
355 if (ret)
356 return ret;
357
358 for (i = 0; i < pm->nr_perflvl; i++) {
359 struct nouveau_pm_level *perflvl = &pm->perflvl[i];
360
361 perflvl->dev_attr.attr.name = perflvl->name;
362 perflvl->dev_attr.attr.mode = S_IRUGO;
363 perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
364 perflvl->dev_attr.store = NULL;
365 sysfs_attr_init(&perflvl->dev_attr.attr);
366
367 ret = device_create_file(d, &perflvl->dev_attr);
368 if (ret) {
Ben Skeggs77145f12012-07-31 16:16:21 +1000369 NV_ERROR(drm, "failed pervlvl %d sysfs: %d\n",
Ben Skeggs330c5982010-09-16 15:39:49 +1000370 perflvl->id, i);
371 perflvl->dev_attr.attr.name = NULL;
372 nouveau_pm_fini(dev);
373 return ret;
374 }
375 }
376
377 return 0;
378}
379
Martin Peres34e9d852010-09-22 20:54:22 +0200380static void
381nouveau_sysfs_fini(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000382{
Ben Skeggs77145f12012-07-31 16:16:21 +1000383 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000384 struct device *d = &dev->pdev->dev;
385 int i;
386
387 device_remove_file(d, &dev_attr_performance_level);
388 for (i = 0; i < pm->nr_perflvl; i++) {
389 struct nouveau_pm_level *pl = &pm->perflvl[i];
390
391 if (!pl->dev_attr.attr.name)
392 break;
393
394 device_remove_file(d, &pl->dev_attr);
395 }
Martin Peres34e9d852010-09-22 20:54:22 +0200396}
397
Ken Milmore658e86e2011-07-03 19:54:28 +0100398#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200399static ssize_t
400nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
401{
402 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200403 struct nouveau_drm *drm = nouveau_drm(dev);
404 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres34e9d852010-09-22 20:54:22 +0200405
Martin Peresaa1b9b42012-09-02 02:55:58 +0200406 return snprintf(buf, PAGE_SIZE, "%d\n", therm->temp_get(therm) * 1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200407}
408static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
409 NULL, 0);
410
411static ssize_t
Martin Peres12e32892012-11-20 01:14:13 +0100412nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d,
413 struct device_attribute *a, char *buf)
414{
415 return snprintf(buf, PAGE_SIZE, "%d\n", 100);
416}
417static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO,
418 nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0);
419
420static ssize_t
421nouveau_hwmon_temp1_auto_point1_temp(struct device *d,
422 struct device_attribute *a, char *buf)
423{
424 struct drm_device *dev = dev_get_drvdata(d);
425 struct nouveau_drm *drm = nouveau_drm(dev);
426 struct nouveau_therm *therm = nouveau_therm(drm->device);
427
428 return snprintf(buf, PAGE_SIZE, "%d\n",
429 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000);
430}
431static ssize_t
432nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d,
433 struct device_attribute *a,
434 const char *buf, size_t count)
435{
436 struct drm_device *dev = dev_get_drvdata(d);
437 struct nouveau_drm *drm = nouveau_drm(dev);
438 struct nouveau_therm *therm = nouveau_therm(drm->device);
439 long value;
440
441 if (kstrtol(buf, 10, &value) == -EINVAL)
442 return count;
443
444 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST,
445 value / 1000);
446
447 return count;
448}
449static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR,
450 nouveau_hwmon_temp1_auto_point1_temp,
451 nouveau_hwmon_set_temp1_auto_point1_temp, 0);
452
453static ssize_t
454nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d,
455 struct device_attribute *a, char *buf)
456{
457 struct drm_device *dev = dev_get_drvdata(d);
458 struct nouveau_drm *drm = nouveau_drm(dev);
459 struct nouveau_therm *therm = nouveau_therm(drm->device);
460
461 return snprintf(buf, PAGE_SIZE, "%d\n",
462 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000);
463}
464static ssize_t
465nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d,
466 struct device_attribute *a,
467 const char *buf, size_t count)
468{
469 struct drm_device *dev = dev_get_drvdata(d);
470 struct nouveau_drm *drm = nouveau_drm(dev);
471 struct nouveau_therm *therm = nouveau_therm(drm->device);
472 long value;
473
474 if (kstrtol(buf, 10, &value) == -EINVAL)
475 return count;
476
477 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST,
478 value / 1000);
479
480 return count;
481}
482static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
483 nouveau_hwmon_temp1_auto_point1_temp_hyst,
484 nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0);
485
486static ssize_t
Martin Peres34e9d852010-09-22 20:54:22 +0200487nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
488{
489 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200490 struct nouveau_drm *drm = nouveau_drm(dev);
491 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres34e9d852010-09-22 20:54:22 +0200492
Martin Peresaa1b9b42012-09-02 02:55:58 +0200493 return snprintf(buf, PAGE_SIZE, "%d\n",
494 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK) * 1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200495}
496static ssize_t
497nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
498 const char *buf, size_t count)
499{
500 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200501 struct nouveau_drm *drm = nouveau_drm(dev);
502 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres34e9d852010-09-22 20:54:22 +0200503 long value;
504
Martin Peresddb20052011-12-17 12:24:59 +0100505 if (kstrtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200506 return count;
507
Martin Peresaa1b9b42012-09-02 02:55:58 +0200508 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK, value / 1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200509
510 return count;
511}
512static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
513 nouveau_hwmon_set_max_temp,
514 0);
515
516static ssize_t
Martin Peres12e32892012-11-20 01:14:13 +0100517nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a,
518 char *buf)
519{
520 struct drm_device *dev = dev_get_drvdata(d);
521 struct nouveau_drm *drm = nouveau_drm(dev);
522 struct nouveau_therm *therm = nouveau_therm(drm->device);
523
524 return snprintf(buf, PAGE_SIZE, "%d\n",
525 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000);
526}
527static ssize_t
528nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a,
529 const char *buf, size_t count)
530{
531 struct drm_device *dev = dev_get_drvdata(d);
532 struct nouveau_drm *drm = nouveau_drm(dev);
533 struct nouveau_therm *therm = nouveau_therm(drm->device);
534 long value;
535
536 if (kstrtol(buf, 10, &value) == -EINVAL)
537 return count;
538
539 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST,
540 value / 1000);
541
542 return count;
543}
544static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
545 nouveau_hwmon_max_temp_hyst,
546 nouveau_hwmon_set_max_temp_hyst, 0);
547
548static ssize_t
Martin Peres34e9d852010-09-22 20:54:22 +0200549nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
550 char *buf)
551{
552 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200553 struct nouveau_drm *drm = nouveau_drm(dev);
554 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres34e9d852010-09-22 20:54:22 +0200555
Martin Peresaa1b9b42012-09-02 02:55:58 +0200556 return snprintf(buf, PAGE_SIZE, "%d\n",
557 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL) * 1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200558}
559static ssize_t
560nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
561 const char *buf,
562 size_t count)
563{
564 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200565 struct nouveau_drm *drm = nouveau_drm(dev);
566 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres34e9d852010-09-22 20:54:22 +0200567 long value;
568
Martin Peresddb20052011-12-17 12:24:59 +0100569 if (kstrtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200570 return count;
571
Martin Peresaa1b9b42012-09-02 02:55:58 +0200572 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL, value / 1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200573
574 return count;
575}
576static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
577 nouveau_hwmon_critical_temp,
578 nouveau_hwmon_set_critical_temp,
579 0);
580
Martin Peres12e32892012-11-20 01:14:13 +0100581static ssize_t
582nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a,
583 char *buf)
584{
585 struct drm_device *dev = dev_get_drvdata(d);
586 struct nouveau_drm *drm = nouveau_drm(dev);
587 struct nouveau_therm *therm = nouveau_therm(drm->device);
588
589 return snprintf(buf, PAGE_SIZE, "%d\n",
590 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000);
591}
592static ssize_t
593nouveau_hwmon_set_critical_temp_hyst(struct device *d,
594 struct device_attribute *a,
595 const char *buf,
596 size_t count)
597{
598 struct drm_device *dev = dev_get_drvdata(d);
599 struct nouveau_drm *drm = nouveau_drm(dev);
600 struct nouveau_therm *therm = nouveau_therm(drm->device);
601 long value;
602
603 if (kstrtol(buf, 10, &value) == -EINVAL)
604 return count;
605
606 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST,
607 value / 1000);
608
609 return count;
610}
611static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
612 nouveau_hwmon_critical_temp_hyst,
613 nouveau_hwmon_set_critical_temp_hyst, 0);
614static ssize_t
615nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a,
616 char *buf)
617{
618 struct drm_device *dev = dev_get_drvdata(d);
619 struct nouveau_drm *drm = nouveau_drm(dev);
620 struct nouveau_therm *therm = nouveau_therm(drm->device);
621
622 return snprintf(buf, PAGE_SIZE, "%d\n",
623 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000);
624}
625static ssize_t
626nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a,
627 const char *buf,
628 size_t count)
629{
630 struct drm_device *dev = dev_get_drvdata(d);
631 struct nouveau_drm *drm = nouveau_drm(dev);
632 struct nouveau_therm *therm = nouveau_therm(drm->device);
633 long value;
634
635 if (kstrtol(buf, 10, &value) == -EINVAL)
636 return count;
637
638 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN, value / 1000);
639
640 return count;
641}
642static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR,
643 nouveau_hwmon_emergency_temp,
644 nouveau_hwmon_set_emergency_temp,
645 0);
646
647static ssize_t
648nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a,
649 char *buf)
650{
651 struct drm_device *dev = dev_get_drvdata(d);
652 struct nouveau_drm *drm = nouveau_drm(dev);
653 struct nouveau_therm *therm = nouveau_therm(drm->device);
654
655 return snprintf(buf, PAGE_SIZE, "%d\n",
656 therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000);
657}
658static ssize_t
659nouveau_hwmon_set_emergency_temp_hyst(struct device *d,
660 struct device_attribute *a,
661 const char *buf,
662 size_t count)
663{
664 struct drm_device *dev = dev_get_drvdata(d);
665 struct nouveau_drm *drm = nouveau_drm(dev);
666 struct nouveau_therm *therm = nouveau_therm(drm->device);
667 long value;
668
669 if (kstrtol(buf, 10, &value) == -EINVAL)
670 return count;
671
672 therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST,
673 value / 1000);
674
675 return count;
676}
677static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR,
678 nouveau_hwmon_emergency_temp_hyst,
679 nouveau_hwmon_set_emergency_temp_hyst,
680 0);
681
Martin Peres34e9d852010-09-22 20:54:22 +0200682static ssize_t nouveau_hwmon_show_name(struct device *dev,
683 struct device_attribute *attr,
684 char *buf)
685{
686 return sprintf(buf, "nouveau\n");
687}
688static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
689
690static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
691 struct device_attribute *attr,
692 char *buf)
693{
694 return sprintf(buf, "1000\n");
695}
696static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
697 nouveau_hwmon_show_update_rate,
698 NULL, 0);
699
Martin Peres11b7d892011-08-15 11:10:30 +1000700static ssize_t
Ben Skeggsb2c36312012-12-06 15:13:06 +1000701nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr,
Martin Peres11b7d892011-08-15 11:10:30 +1000702 char *buf)
703{
704 struct drm_device *dev = dev_get_drvdata(d);
Ben Skeggs77145f12012-07-31 16:16:21 +1000705 struct nouveau_drm *drm = nouveau_drm(dev);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200706 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres11b7d892011-08-15 11:10:30 +1000707
Martin Peresaa1b9b42012-09-02 02:55:58 +0200708 return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm));
Martin Peres11b7d892011-08-15 11:10:30 +1000709}
Ben Skeggsb2c36312012-12-06 15:13:06 +1000710static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input,
Martin Peres11b7d892011-08-15 11:10:30 +1000711 NULL, 0);
712
Martin Peres2f951a52012-09-04 13:52:00 +0200713 static ssize_t
714nouveau_hwmon_get_pwm1_enable(struct device *d,
715 struct device_attribute *a, char *buf)
Martin Peres11b7d892011-08-15 11:10:30 +1000716{
717 struct drm_device *dev = dev_get_drvdata(d);
Martin Peres2f951a52012-09-04 13:52:00 +0200718 struct nouveau_drm *drm = nouveau_drm(dev);
719 struct nouveau_therm *therm = nouveau_therm(drm->device);
Ben Skeggsa1750942011-09-17 01:42:12 +1000720 int ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000721
Martin Peres2f951a52012-09-04 13:52:00 +0200722 ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MODE);
Martin Peres11b7d892011-08-15 11:10:30 +1000723 if (ret < 0)
724 return ret;
725
726 return sprintf(buf, "%i\n", ret);
727}
728
729static ssize_t
Martin Peres2f951a52012-09-04 13:52:00 +0200730nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a,
731 const char *buf, size_t count)
732{
733 struct drm_device *dev = dev_get_drvdata(d);
734 struct nouveau_drm *drm = nouveau_drm(dev);
735 struct nouveau_therm *therm = nouveau_therm(drm->device);
736 long value;
737 int ret;
738
739 if (strict_strtol(buf, 10, &value) == -EINVAL)
740 return -EINVAL;
741
742 ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MODE, value);
743 if (ret)
744 return ret;
745 else
746 return count;
747}
748static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
749 nouveau_hwmon_get_pwm1_enable,
750 nouveau_hwmon_set_pwm1_enable, 0);
751
Martin Peres11b7d892011-08-15 11:10:30 +1000752static ssize_t
Martin Peresc9cbf132012-09-04 13:39:40 +0200753nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf)
Martin Peres11b7d892011-08-15 11:10:30 +1000754{
755 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200756 struct nouveau_drm *drm = nouveau_drm(dev);
757 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres11b7d892011-08-15 11:10:30 +1000758 int ret;
759
Martin Peresaa1b9b42012-09-02 02:55:58 +0200760 ret = therm->fan_get(therm);
Martin Peres11b7d892011-08-15 11:10:30 +1000761 if (ret < 0)
762 return ret;
763
764 return sprintf(buf, "%i\n", ret);
765}
766
767static ssize_t
Martin Peresc9cbf132012-09-04 13:39:40 +0200768nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a,
Martin Peres11b7d892011-08-15 11:10:30 +1000769 const char *buf, size_t count)
770{
771 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200772 struct nouveau_drm *drm = nouveau_drm(dev);
773 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres11b7d892011-08-15 11:10:30 +1000774 int ret = -ENODEV;
775 long value;
776
777 if (nouveau_perflvl_wr != 7777)
778 return -EPERM;
779
Martin Peresddb20052011-12-17 12:24:59 +0100780 if (kstrtol(buf, 10, &value) == -EINVAL)
Martin Peres11b7d892011-08-15 11:10:30 +1000781 return -EINVAL;
782
Martin Peresaa1b9b42012-09-02 02:55:58 +0200783 ret = therm->fan_set(therm, value);
Martin Peres11b7d892011-08-15 11:10:30 +1000784 if (ret)
785 return ret;
786
787 return count;
788}
789
Martin Peresc9cbf132012-09-04 13:39:40 +0200790static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR,
791 nouveau_hwmon_get_pwm1,
792 nouveau_hwmon_set_pwm1, 0);
Martin Peres11b7d892011-08-15 11:10:30 +1000793
794static ssize_t
Martin Peresc9cbf132012-09-04 13:39:40 +0200795nouveau_hwmon_get_pwm1_min(struct device *d,
Martin Peres11b7d892011-08-15 11:10:30 +1000796 struct device_attribute *a, char *buf)
797{
798 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200799 struct nouveau_drm *drm = nouveau_drm(dev);
800 struct nouveau_therm *therm = nouveau_therm(drm->device);
801 int ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000802
Martin Peresaa1b9b42012-09-02 02:55:58 +0200803 ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY);
804 if (ret < 0)
805 return ret;
806
807 return sprintf(buf, "%i\n", ret);
Martin Peres11b7d892011-08-15 11:10:30 +1000808}
809
810static ssize_t
Martin Peresc9cbf132012-09-04 13:39:40 +0200811nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a,
Martin Peres11b7d892011-08-15 11:10:30 +1000812 const char *buf, size_t count)
813{
814 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200815 struct nouveau_drm *drm = nouveau_drm(dev);
816 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres11b7d892011-08-15 11:10:30 +1000817 long value;
Martin Peresaa1b9b42012-09-02 02:55:58 +0200818 int ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000819
Martin Peresddb20052011-12-17 12:24:59 +0100820 if (kstrtol(buf, 10, &value) == -EINVAL)
Martin Peres11b7d892011-08-15 11:10:30 +1000821 return -EINVAL;
822
Martin Peresaa1b9b42012-09-02 02:55:58 +0200823 ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY, value);
824 if (ret < 0)
825 return ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000826
827 return count;
828}
829
Martin Peresc9cbf132012-09-04 13:39:40 +0200830static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR,
831 nouveau_hwmon_get_pwm1_min,
832 nouveau_hwmon_set_pwm1_min, 0);
Martin Peres11b7d892011-08-15 11:10:30 +1000833
834static ssize_t
Martin Peresc9cbf132012-09-04 13:39:40 +0200835nouveau_hwmon_get_pwm1_max(struct device *d,
Martin Peres11b7d892011-08-15 11:10:30 +1000836 struct device_attribute *a, char *buf)
837{
838 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200839 struct nouveau_drm *drm = nouveau_drm(dev);
840 struct nouveau_therm *therm = nouveau_therm(drm->device);
841 int ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000842
Martin Peresaa1b9b42012-09-02 02:55:58 +0200843 ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY);
844 if (ret < 0)
845 return ret;
846
847 return sprintf(buf, "%i\n", ret);
Martin Peres11b7d892011-08-15 11:10:30 +1000848}
849
850static ssize_t
Martin Peresc9cbf132012-09-04 13:39:40 +0200851nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a,
Martin Peres11b7d892011-08-15 11:10:30 +1000852 const char *buf, size_t count)
853{
854 struct drm_device *dev = dev_get_drvdata(d);
Martin Peresaa1b9b42012-09-02 02:55:58 +0200855 struct nouveau_drm *drm = nouveau_drm(dev);
856 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres11b7d892011-08-15 11:10:30 +1000857 long value;
Martin Peresaa1b9b42012-09-02 02:55:58 +0200858 int ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000859
Martin Peresddb20052011-12-17 12:24:59 +0100860 if (kstrtol(buf, 10, &value) == -EINVAL)
Martin Peres11b7d892011-08-15 11:10:30 +1000861 return -EINVAL;
862
Martin Peresaa1b9b42012-09-02 02:55:58 +0200863 ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY, value);
864 if (ret < 0)
865 return ret;
Martin Peres11b7d892011-08-15 11:10:30 +1000866
867 return count;
868}
869
Martin Peresc9cbf132012-09-04 13:39:40 +0200870static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR,
871 nouveau_hwmon_get_pwm1_max,
872 nouveau_hwmon_set_pwm1_max, 0);
Martin Peres11b7d892011-08-15 11:10:30 +1000873
Martin Peres34e9d852010-09-22 20:54:22 +0200874static struct attribute *hwmon_attributes[] = {
875 &sensor_dev_attr_temp1_input.dev_attr.attr,
Martin Peres12e32892012-11-20 01:14:13 +0100876 &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,
877 &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
878 &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
Martin Peres34e9d852010-09-22 20:54:22 +0200879 &sensor_dev_attr_temp1_max.dev_attr.attr,
Martin Peres12e32892012-11-20 01:14:13 +0100880 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
Martin Peres34e9d852010-09-22 20:54:22 +0200881 &sensor_dev_attr_temp1_crit.dev_attr.attr,
Martin Peres12e32892012-11-20 01:14:13 +0100882 &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
883 &sensor_dev_attr_temp1_emergency.dev_attr.attr,
884 &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr,
Martin Peres34e9d852010-09-22 20:54:22 +0200885 &sensor_dev_attr_name.dev_attr.attr,
886 &sensor_dev_attr_update_rate.dev_attr.attr,
887 NULL
888};
Martin Peres11b7d892011-08-15 11:10:30 +1000889static struct attribute *hwmon_fan_rpm_attributes[] = {
Ben Skeggsb2c36312012-12-06 15:13:06 +1000890 &sensor_dev_attr_fan1_input.dev_attr.attr,
Martin Peres11b7d892011-08-15 11:10:30 +1000891 NULL
892};
893static struct attribute *hwmon_pwm_fan_attributes[] = {
Martin Peres2f951a52012-09-04 13:52:00 +0200894 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Martin Peresc9cbf132012-09-04 13:39:40 +0200895 &sensor_dev_attr_pwm1.dev_attr.attr,
896 &sensor_dev_attr_pwm1_min.dev_attr.attr,
897 &sensor_dev_attr_pwm1_max.dev_attr.attr,
Martin Peres11b7d892011-08-15 11:10:30 +1000898 NULL
899};
Martin Peres34e9d852010-09-22 20:54:22 +0200900
901static const struct attribute_group hwmon_attrgroup = {
902 .attrs = hwmon_attributes,
903};
Martin Peres11b7d892011-08-15 11:10:30 +1000904static const struct attribute_group hwmon_fan_rpm_attrgroup = {
905 .attrs = hwmon_fan_rpm_attributes,
906};
907static const struct attribute_group hwmon_pwm_fan_attrgroup = {
908 .attrs = hwmon_pwm_fan_attributes,
909};
Martin Peresb54262f2010-10-26 12:48:28 +0200910#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200911
912static int
913nouveau_hwmon_init(struct drm_device *dev)
914{
Ben Skeggs77145f12012-07-31 16:16:21 +1000915 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs77145f12012-07-31 16:16:21 +1000916
Dave Airlie095f9792012-01-10 10:13:16 +0000917#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Guenter Roeck57cdf822013-01-01 03:28:00 -0800918 struct nouveau_drm *drm = nouveau_drm(dev);
919 struct nouveau_therm *therm = nouveau_therm(drm->device);
Martin Peres34e9d852010-09-22 20:54:22 +0200920 struct device *hwmon_dev;
Martin Peres11b7d892011-08-15 11:10:30 +1000921 int ret = 0;
Martin Peres34e9d852010-09-22 20:54:22 +0200922
Martin Peresa5f5af82012-10-04 00:28:21 +0200923 if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set)
Francisco Jerez8155cac2010-09-23 20:58:38 +0200924 return -ENODEV;
Martin Peres34e9d852010-09-22 20:54:22 +0200925
926 hwmon_dev = hwmon_device_register(&dev->pdev->dev);
927 if (IS_ERR(hwmon_dev)) {
928 ret = PTR_ERR(hwmon_dev);
Ben Skeggs77145f12012-07-31 16:16:21 +1000929 NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret);
Martin Peres34e9d852010-09-22 20:54:22 +0200930 return ret;
931 }
932 dev_set_drvdata(hwmon_dev, dev);
Martin Peres11b7d892011-08-15 11:10:30 +1000933
934 /* default sysfs entries */
Lucas Stach07cfe0e2011-01-06 20:29:53 +0100935 ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Martin Peres34e9d852010-09-22 20:54:22 +0200936 if (ret) {
Martin Peres11b7d892011-08-15 11:10:30 +1000937 if (ret)
938 goto error;
939 }
940
941 /* if the card has a pwm fan */
942 /*XXX: incorrect, need better detection for this, some boards have
943 * the gpio entries for pwm fan control even when there's no
944 * actual fan connected to it... therm table? */
Martin Peresaa1b9b42012-09-02 02:55:58 +0200945 if (therm->fan_get && therm->fan_get(therm) >= 0) {
Martin Peres11b7d892011-08-15 11:10:30 +1000946 ret = sysfs_create_group(&dev->pdev->dev.kobj,
947 &hwmon_pwm_fan_attrgroup);
948 if (ret)
949 goto error;
950 }
951
952 /* if the card can read the fan rpm */
Martin Peresaa1b9b42012-09-02 02:55:58 +0200953 if (therm->fan_sense(therm) >= 0) {
Martin Peres11b7d892011-08-15 11:10:30 +1000954 ret = sysfs_create_group(&dev->pdev->dev.kobj,
955 &hwmon_fan_rpm_attrgroup);
956 if (ret)
957 goto error;
Martin Peres34e9d852010-09-22 20:54:22 +0200958 }
959
Francisco Jerez8155cac2010-09-23 20:58:38 +0200960 pm->hwmon = hwmon_dev;
Martin Peres11b7d892011-08-15 11:10:30 +1000961
Martin Peres34e9d852010-09-22 20:54:22 +0200962 return 0;
Martin Peres11b7d892011-08-15 11:10:30 +1000963
964error:
Ben Skeggs77145f12012-07-31 16:16:21 +1000965 NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret);
Martin Peres11b7d892011-08-15 11:10:30 +1000966 hwmon_device_unregister(hwmon_dev);
967 pm->hwmon = NULL;
968 return ret;
969#else
970 pm->hwmon = NULL;
971 return 0;
972#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200973}
974
975static void
976nouveau_hwmon_fini(struct drm_device *dev)
977{
Ken Milmore658e86e2011-07-03 19:54:28 +0100978#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Ben Skeggs77145f12012-07-31 16:16:21 +1000979 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200980
Francisco Jerez8155cac2010-09-23 20:58:38 +0200981 if (pm->hwmon) {
Lucas Stach8c06a3e2011-01-30 10:54:11 +0100982 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Martin Peresddb20052011-12-17 12:24:59 +0100983 sysfs_remove_group(&dev->pdev->dev.kobj,
984 &hwmon_pwm_fan_attrgroup);
985 sysfs_remove_group(&dev->pdev->dev.kobj,
986 &hwmon_fan_rpm_attrgroup);
Martin Peres11b7d892011-08-15 11:10:30 +1000987
Francisco Jerez8155cac2010-09-23 20:58:38 +0200988 hwmon_device_unregister(pm->hwmon);
Martin Peres34e9d852010-09-22 20:54:22 +0200989 }
Martin Peresb54262f2010-10-26 12:48:28 +0200990#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200991}
992
Martin Peres1f962792011-04-12 00:55:44 +0200993#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000994static int
995nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
996{
Ben Skeggs77145f12012-07-31 16:16:21 +1000997 struct nouveau_pm *pm = container_of(nb, struct nouveau_pm, acpi_nb);
998 struct nouveau_drm *drm = nouveau_drm(pm->dev);
Ben Skeggs60326492010-10-12 12:31:32 +1000999 struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
1000
1001 if (strcmp(entry->device_class, "ac_adapter") == 0) {
1002 bool ac = power_supply_is_system_supplied();
1003
Ben Skeggs77145f12012-07-31 16:16:21 +10001004 NV_DEBUG(drm, "power supply changed: %s\n", ac ? "AC" : "DC");
1005 nouveau_pm_trigger(pm->dev);
Ben Skeggs60326492010-10-12 12:31:32 +10001006 }
1007
1008 return NOTIFY_OK;
1009}
1010#endif
1011
Martin Peres34e9d852010-09-22 20:54:22 +02001012int
1013nouveau_pm_init(struct drm_device *dev)
1014{
Ben Skeggs77145f12012-07-31 16:16:21 +10001015 struct nouveau_device *device = nouveau_dev(dev);
1016 struct nouveau_drm *drm = nouveau_drm(dev);
1017 struct nouveau_pm *pm;
Martin Peres34e9d852010-09-22 20:54:22 +02001018 char info[256];
1019 int ret, i;
1020
Ben Skeggs77145f12012-07-31 16:16:21 +10001021 pm = drm->pm = kzalloc(sizeof(*pm), GFP_KERNEL);
1022 if (!pm)
1023 return -ENOMEM;
1024
1025 pm->dev = dev;
1026
1027 if (device->card_type < NV_40) {
1028 pm->clocks_get = nv04_pm_clocks_get;
1029 pm->clocks_pre = nv04_pm_clocks_pre;
1030 pm->clocks_set = nv04_pm_clocks_set;
1031 if (nouveau_gpio(drm->device)) {
1032 pm->voltage_get = nouveau_voltage_gpio_get;
1033 pm->voltage_set = nouveau_voltage_gpio_set;
1034 }
1035 } else
1036 if (device->card_type < NV_50) {
1037 pm->clocks_get = nv40_pm_clocks_get;
1038 pm->clocks_pre = nv40_pm_clocks_pre;
1039 pm->clocks_set = nv40_pm_clocks_set;
1040 pm->voltage_get = nouveau_voltage_gpio_get;
1041 pm->voltage_set = nouveau_voltage_gpio_set;
Ben Skeggs77145f12012-07-31 16:16:21 +10001042 } else
1043 if (device->card_type < NV_C0) {
1044 if (device->chipset < 0xa3 ||
1045 device->chipset == 0xaa ||
1046 device->chipset == 0xac) {
1047 pm->clocks_get = nv50_pm_clocks_get;
1048 pm->clocks_pre = nv50_pm_clocks_pre;
1049 pm->clocks_set = nv50_pm_clocks_set;
1050 } else {
1051 pm->clocks_get = nva3_pm_clocks_get;
1052 pm->clocks_pre = nva3_pm_clocks_pre;
1053 pm->clocks_set = nva3_pm_clocks_set;
1054 }
1055 pm->voltage_get = nouveau_voltage_gpio_get;
1056 pm->voltage_set = nouveau_voltage_gpio_set;
Ben Skeggs77145f12012-07-31 16:16:21 +10001057 } else
1058 if (device->card_type < NV_E0) {
1059 pm->clocks_get = nvc0_pm_clocks_get;
1060 pm->clocks_pre = nvc0_pm_clocks_pre;
1061 pm->clocks_set = nvc0_pm_clocks_set;
1062 pm->voltage_get = nouveau_voltage_gpio_get;
1063 pm->voltage_set = nouveau_voltage_gpio_set;
Ben Skeggs77145f12012-07-31 16:16:21 +10001064 }
1065
1066
Ben Skeggs68a64ca2012-01-23 13:47:02 +10001067 /* parse aux tables from vbios */
Martin Peres34e9d852010-09-22 20:54:22 +02001068 nouveau_volt_init(dev);
Martin Peres34e9d852010-09-22 20:54:22 +02001069
Dmitry Eremin-Solenikovd89c8ce2012-09-25 10:31:40 +04001070 INIT_LIST_HEAD(&pm->profiles);
Martin Peres34e9d852010-09-22 20:54:22 +02001071
Ben Skeggs68a64ca2012-01-23 13:47:02 +10001072 /* determine current ("boot") performance level */
1073 ret = nouveau_pm_perflvl_get(dev, &pm->boot);
1074 if (ret) {
Ben Skeggs77145f12012-07-31 16:16:21 +10001075 NV_ERROR(drm, "failed to determine boot perflvl\n");
Ben Skeggs68a64ca2012-01-23 13:47:02 +10001076 return ret;
1077 }
1078
1079 strncpy(pm->boot.name, "boot", 4);
Ben Skeggs8d7bb402012-01-24 15:59:07 +10001080 strncpy(pm->boot.profile.name, "boot", 4);
1081 pm->boot.profile.func = &nouveau_pm_static_profile_func;
1082
Ben Skeggs8d7bb402012-01-24 15:59:07 +10001083 list_add(&pm->boot.profile.head, &pm->profiles);
1084
1085 pm->profile_ac = &pm->boot.profile;
1086 pm->profile_dc = &pm->boot.profile;
Ben Skeggs25c53c12012-01-24 18:03:25 +10001087 pm->profile = &pm->boot.profile;
Ben Skeggs68a64ca2012-01-23 13:47:02 +10001088 pm->cur = &pm->boot;
1089
1090 /* add performance levels from vbios */
1091 nouveau_perf_init(dev);
1092
1093 /* display available performance levels */
Ben Skeggs77145f12012-07-31 16:16:21 +10001094 NV_INFO(drm, "%d available performance level(s)\n", pm->nr_perflvl);
Martin Peres34e9d852010-09-22 20:54:22 +02001095 for (i = 0; i < pm->nr_perflvl; i++) {
1096 nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
Ben Skeggs77145f12012-07-31 16:16:21 +10001097 NV_INFO(drm, "%d:%s", pm->perflvl[i].id, info);
Martin Peres34e9d852010-09-22 20:54:22 +02001098 }
1099
Ben Skeggs68a64ca2012-01-23 13:47:02 +10001100 nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
Ben Skeggs77145f12012-07-31 16:16:21 +10001101 NV_INFO(drm, "c:%s", info);
Martin Peres34e9d852010-09-22 20:54:22 +02001102
1103 /* switch performance levels now if requested */
Ben Skeggs8d7bb402012-01-24 15:59:07 +10001104 if (nouveau_perflvl != NULL)
1105 nouveau_pm_profile_set(dev, nouveau_perflvl);
Martin Peres34e9d852010-09-22 20:54:22 +02001106
1107 nouveau_sysfs_init(dev);
1108 nouveau_hwmon_init(dev);
Martin Peres1f962792011-04-12 00:55:44 +02001109#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +10001110 pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
1111 register_acpi_notifier(&pm->acpi_nb);
1112#endif
Martin Peres34e9d852010-09-22 20:54:22 +02001113
1114 return 0;
1115}
1116
1117void
1118nouveau_pm_fini(struct drm_device *dev)
1119{
Ben Skeggs77145f12012-07-31 16:16:21 +10001120 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs25c53c12012-01-24 18:03:25 +10001121 struct nouveau_pm_profile *profile, *tmp;
1122
1123 list_for_each_entry_safe(profile, tmp, &pm->profiles, head) {
1124 list_del(&profile->head);
1125 profile->func->destroy(profile);
1126 }
Martin Peres34e9d852010-09-22 20:54:22 +02001127
1128 if (pm->cur != &pm->boot)
1129 nouveau_pm_perflvl_set(dev, &pm->boot);
Ben Skeggs330c5982010-09-16 15:39:49 +10001130
1131 nouveau_perf_fini(dev);
1132 nouveau_volt_fini(dev);
Martin Peres34e9d852010-09-22 20:54:22 +02001133
Martin Peres1f962792011-04-12 00:55:44 +02001134#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +10001135 unregister_acpi_notifier(&pm->acpi_nb);
1136#endif
Martin Peres34e9d852010-09-22 20:54:22 +02001137 nouveau_hwmon_fini(dev);
1138 nouveau_sysfs_fini(dev);
Ben Skeggs77145f12012-07-31 16:16:21 +10001139
1140 nouveau_drm(dev)->pm = NULL;
1141 kfree(pm);
Ben Skeggs330c5982010-09-16 15:39:49 +10001142}
1143
Ben Skeggs64f1c112010-09-17 13:35:25 +10001144void
1145nouveau_pm_resume(struct drm_device *dev)
1146{
Ben Skeggs77145f12012-07-31 16:16:21 +10001147 struct nouveau_pm *pm = nouveau_pm(dev);
Ben Skeggs64f1c112010-09-17 13:35:25 +10001148 struct nouveau_pm_level *perflvl;
1149
Ben Skeggs317495b2011-02-17 11:11:28 +10001150 if (!pm->cur || pm->cur == &pm->boot)
Ben Skeggs64f1c112010-09-17 13:35:25 +10001151 return;
1152
1153 perflvl = pm->cur;
1154 pm->cur = &pm->boot;
1155 nouveau_pm_perflvl_set(dev, perflvl);
1156}