Commit | Line | Data |
---|---|---|
330c5988 BS |
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 | ||
6032649d BS |
25 | #ifdef CONFIG_ACPI |
26 | #include <linux/acpi.h> | |
27 | #endif | |
28 | #include <linux/power_supply.h> | |
34e9d85a MP |
29 | #include <linux/hwmon.h> |
30 | #include <linux/hwmon-sysfs.h> | |
31 | ||
612a9aab | 32 | #include <drm/drmP.h> |
a175094c | 33 | |
77145f1c | 34 | #include "nouveau_drm.h" |
b9ed919f | 35 | #include "nouveau_hwmon.h" |
a175094c | 36 | |
77145f1c BS |
37 | #include <subdev/gpio.h> |
38 | #include <subdev/timer.h> | |
aa1b9b48 | 39 | #include <subdev/therm.h> |
a175094c | 40 | |
658e86ee | 41 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
34e9d85a MP |
42 | static ssize_t |
43 | nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) | |
44 | { | |
45 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
46 | struct nouveau_drm *drm = nouveau_drm(dev); |
47 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
804ca90f | 48 | int temp = therm->temp_get(therm); |
34e9d85a | 49 | |
804ca90f MP |
50 | if (temp < 0) |
51 | return temp; | |
52 | ||
53 | return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000); | |
34e9d85a MP |
54 | } |
55 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, | |
56 | NULL, 0); | |
57 | ||
12e32896 MP |
58 | static ssize_t |
59 | nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, | |
60 | struct device_attribute *a, char *buf) | |
61 | { | |
62 | return snprintf(buf, PAGE_SIZE, "%d\n", 100); | |
63 | } | |
64 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO, | |
65 | nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0); | |
66 | ||
67 | static ssize_t | |
68 | nouveau_hwmon_temp1_auto_point1_temp(struct device *d, | |
69 | struct device_attribute *a, char *buf) | |
70 | { | |
71 | struct drm_device *dev = dev_get_drvdata(d); | |
72 | struct nouveau_drm *drm = nouveau_drm(dev); | |
73 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
74 | ||
75 | return snprintf(buf, PAGE_SIZE, "%d\n", | |
76 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000); | |
77 | } | |
78 | static ssize_t | |
79 | nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d, | |
80 | struct device_attribute *a, | |
81 | const char *buf, size_t count) | |
82 | { | |
83 | struct drm_device *dev = dev_get_drvdata(d); | |
84 | struct nouveau_drm *drm = nouveau_drm(dev); | |
85 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
86 | long value; | |
87 | ||
88 | if (kstrtol(buf, 10, &value) == -EINVAL) | |
89 | return count; | |
90 | ||
91 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST, | |
92 | value / 1000); | |
93 | ||
94 | return count; | |
95 | } | |
96 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR, | |
97 | nouveau_hwmon_temp1_auto_point1_temp, | |
98 | nouveau_hwmon_set_temp1_auto_point1_temp, 0); | |
99 | ||
100 | static ssize_t | |
101 | nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d, | |
102 | struct device_attribute *a, char *buf) | |
103 | { | |
104 | struct drm_device *dev = dev_get_drvdata(d); | |
105 | struct nouveau_drm *drm = nouveau_drm(dev); | |
106 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
107 | ||
108 | return snprintf(buf, PAGE_SIZE, "%d\n", | |
109 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000); | |
110 | } | |
111 | static ssize_t | |
112 | nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d, | |
113 | struct device_attribute *a, | |
114 | const char *buf, size_t count) | |
115 | { | |
116 | struct drm_device *dev = dev_get_drvdata(d); | |
117 | struct nouveau_drm *drm = nouveau_drm(dev); | |
118 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
119 | long value; | |
120 | ||
121 | if (kstrtol(buf, 10, &value) == -EINVAL) | |
122 | return count; | |
123 | ||
124 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST, | |
125 | value / 1000); | |
126 | ||
127 | return count; | |
128 | } | |
129 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, | |
130 | nouveau_hwmon_temp1_auto_point1_temp_hyst, | |
131 | nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0); | |
132 | ||
34e9d85a MP |
133 | static ssize_t |
134 | nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) | |
135 | { | |
136 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
137 | struct nouveau_drm *drm = nouveau_drm(dev); |
138 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
34e9d85a | 139 | |
aa1b9b48 MP |
140 | return snprintf(buf, PAGE_SIZE, "%d\n", |
141 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK) * 1000); | |
34e9d85a MP |
142 | } |
143 | static ssize_t | |
144 | nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, | |
145 | const char *buf, size_t count) | |
146 | { | |
147 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
148 | struct nouveau_drm *drm = nouveau_drm(dev); |
149 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
34e9d85a MP |
150 | long value; |
151 | ||
ddb20055 | 152 | if (kstrtol(buf, 10, &value) == -EINVAL) |
34e9d85a MP |
153 | return count; |
154 | ||
aa1b9b48 | 155 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK, value / 1000); |
34e9d85a MP |
156 | |
157 | return count; | |
158 | } | |
159 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, | |
160 | nouveau_hwmon_set_max_temp, | |
161 | 0); | |
162 | ||
12e32896 MP |
163 | static ssize_t |
164 | nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a, | |
165 | char *buf) | |
166 | { | |
167 | struct drm_device *dev = dev_get_drvdata(d); | |
168 | struct nouveau_drm *drm = nouveau_drm(dev); | |
169 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
170 | ||
171 | return snprintf(buf, PAGE_SIZE, "%d\n", | |
172 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000); | |
173 | } | |
174 | static ssize_t | |
175 | nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a, | |
176 | const char *buf, size_t count) | |
177 | { | |
178 | struct drm_device *dev = dev_get_drvdata(d); | |
179 | struct nouveau_drm *drm = nouveau_drm(dev); | |
180 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
181 | long value; | |
182 | ||
183 | if (kstrtol(buf, 10, &value) == -EINVAL) | |
184 | return count; | |
185 | ||
186 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST, | |
187 | value / 1000); | |
188 | ||
189 | return count; | |
190 | } | |
191 | static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, | |
192 | nouveau_hwmon_max_temp_hyst, | |
193 | nouveau_hwmon_set_max_temp_hyst, 0); | |
194 | ||
34e9d85a MP |
195 | static ssize_t |
196 | nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, | |
197 | char *buf) | |
198 | { | |
199 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
200 | struct nouveau_drm *drm = nouveau_drm(dev); |
201 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
34e9d85a | 202 | |
aa1b9b48 MP |
203 | return snprintf(buf, PAGE_SIZE, "%d\n", |
204 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL) * 1000); | |
34e9d85a MP |
205 | } |
206 | static ssize_t | |
207 | nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, | |
208 | const char *buf, | |
209 | size_t count) | |
210 | { | |
211 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
212 | struct nouveau_drm *drm = nouveau_drm(dev); |
213 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
34e9d85a MP |
214 | long value; |
215 | ||
ddb20055 | 216 | if (kstrtol(buf, 10, &value) == -EINVAL) |
34e9d85a MP |
217 | return count; |
218 | ||
aa1b9b48 | 219 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL, value / 1000); |
34e9d85a MP |
220 | |
221 | return count; | |
222 | } | |
223 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, | |
224 | nouveau_hwmon_critical_temp, | |
225 | nouveau_hwmon_set_critical_temp, | |
226 | 0); | |
227 | ||
12e32896 MP |
228 | static ssize_t |
229 | nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a, | |
230 | char *buf) | |
231 | { | |
232 | struct drm_device *dev = dev_get_drvdata(d); | |
233 | struct nouveau_drm *drm = nouveau_drm(dev); | |
234 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
235 | ||
236 | return snprintf(buf, PAGE_SIZE, "%d\n", | |
237 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000); | |
238 | } | |
239 | static ssize_t | |
240 | nouveau_hwmon_set_critical_temp_hyst(struct device *d, | |
241 | struct device_attribute *a, | |
242 | const char *buf, | |
243 | size_t count) | |
244 | { | |
245 | struct drm_device *dev = dev_get_drvdata(d); | |
246 | struct nouveau_drm *drm = nouveau_drm(dev); | |
247 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
248 | long value; | |
249 | ||
250 | if (kstrtol(buf, 10, &value) == -EINVAL) | |
251 | return count; | |
252 | ||
253 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST, | |
254 | value / 1000); | |
255 | ||
256 | return count; | |
257 | } | |
258 | static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, | |
259 | nouveau_hwmon_critical_temp_hyst, | |
260 | nouveau_hwmon_set_critical_temp_hyst, 0); | |
261 | static ssize_t | |
262 | nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a, | |
263 | char *buf) | |
264 | { | |
265 | struct drm_device *dev = dev_get_drvdata(d); | |
266 | struct nouveau_drm *drm = nouveau_drm(dev); | |
267 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
268 | ||
269 | return snprintf(buf, PAGE_SIZE, "%d\n", | |
270 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000); | |
271 | } | |
272 | static ssize_t | |
273 | nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a, | |
274 | const char *buf, | |
275 | size_t count) | |
276 | { | |
277 | struct drm_device *dev = dev_get_drvdata(d); | |
278 | struct nouveau_drm *drm = nouveau_drm(dev); | |
279 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
280 | long value; | |
281 | ||
282 | if (kstrtol(buf, 10, &value) == -EINVAL) | |
283 | return count; | |
284 | ||
285 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN, value / 1000); | |
286 | ||
287 | return count; | |
288 | } | |
289 | static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR, | |
290 | nouveau_hwmon_emergency_temp, | |
291 | nouveau_hwmon_set_emergency_temp, | |
292 | 0); | |
293 | ||
294 | static ssize_t | |
295 | nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a, | |
296 | char *buf) | |
297 | { | |
298 | struct drm_device *dev = dev_get_drvdata(d); | |
299 | struct nouveau_drm *drm = nouveau_drm(dev); | |
300 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
301 | ||
302 | return snprintf(buf, PAGE_SIZE, "%d\n", | |
303 | therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000); | |
304 | } | |
305 | static ssize_t | |
306 | nouveau_hwmon_set_emergency_temp_hyst(struct device *d, | |
307 | struct device_attribute *a, | |
308 | const char *buf, | |
309 | size_t count) | |
310 | { | |
311 | struct drm_device *dev = dev_get_drvdata(d); | |
312 | struct nouveau_drm *drm = nouveau_drm(dev); | |
313 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
314 | long value; | |
315 | ||
316 | if (kstrtol(buf, 10, &value) == -EINVAL) | |
317 | return count; | |
318 | ||
319 | therm->attr_set(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST, | |
320 | value / 1000); | |
321 | ||
322 | return count; | |
323 | } | |
324 | static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR, | |
325 | nouveau_hwmon_emergency_temp_hyst, | |
326 | nouveau_hwmon_set_emergency_temp_hyst, | |
327 | 0); | |
328 | ||
34e9d85a MP |
329 | static ssize_t nouveau_hwmon_show_name(struct device *dev, |
330 | struct device_attribute *attr, | |
331 | char *buf) | |
332 | { | |
333 | return sprintf(buf, "nouveau\n"); | |
334 | } | |
335 | static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); | |
336 | ||
337 | static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, | |
338 | struct device_attribute *attr, | |
339 | char *buf) | |
340 | { | |
341 | return sprintf(buf, "1000\n"); | |
342 | } | |
343 | static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, | |
344 | nouveau_hwmon_show_update_rate, | |
345 | NULL, 0); | |
346 | ||
11b7d895 | 347 | static ssize_t |
b2c36312 | 348 | nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr, |
11b7d895 MP |
349 | char *buf) |
350 | { | |
351 | struct drm_device *dev = dev_get_drvdata(d); | |
77145f1c | 352 | struct nouveau_drm *drm = nouveau_drm(dev); |
aa1b9b48 | 353 | struct nouveau_therm *therm = nouveau_therm(drm->device); |
11b7d895 | 354 | |
aa1b9b48 | 355 | return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm)); |
11b7d895 | 356 | } |
b2c36312 | 357 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input, |
11b7d895 MP |
358 | NULL, 0); |
359 | ||
2f951a5d MP |
360 | static ssize_t |
361 | nouveau_hwmon_get_pwm1_enable(struct device *d, | |
362 | struct device_attribute *a, char *buf) | |
363 | { | |
364 | struct drm_device *dev = dev_get_drvdata(d); | |
365 | struct nouveau_drm *drm = nouveau_drm(dev); | |
366 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
a0b25635 | 367 | int ret; |
11b7d895 | 368 | |
2f951a5d MP |
369 | ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MODE); |
370 | if (ret < 0) | |
a0b25635 | 371 | return ret; |
11b7d895 | 372 | |
2f951a5d MP |
373 | return sprintf(buf, "%i\n", ret); |
374 | } | |
11b7d895 | 375 | |
2f951a5d MP |
376 | static ssize_t |
377 | nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a, | |
378 | const char *buf, size_t count) | |
379 | { | |
380 | struct drm_device *dev = dev_get_drvdata(d); | |
381 | struct nouveau_drm *drm = nouveau_drm(dev); | |
382 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
383 | long value; | |
384 | int ret; | |
385 | ||
0ac4e3a5 JH |
386 | ret = kstrtol(buf, 10, &value); |
387 | if (ret) | |
388 | return ret; | |
11b7d895 | 389 | |
2f951a5d MP |
390 | ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MODE, value); |
391 | if (ret) | |
392 | return ret; | |
393 | else | |
394 | return count; | |
11b7d895 | 395 | } |
2f951a5d MP |
396 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, |
397 | nouveau_hwmon_get_pwm1_enable, | |
398 | nouveau_hwmon_set_pwm1_enable, 0); | |
11b7d895 MP |
399 | |
400 | static ssize_t | |
c9cbf135 | 401 | nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf) |
11b7d895 MP |
402 | { |
403 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
404 | struct nouveau_drm *drm = nouveau_drm(dev); |
405 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
a175094c | 406 | int ret; |
11b7d895 | 407 | |
aa1b9b48 | 408 | ret = therm->fan_get(therm); |
11b7d895 MP |
409 | if (ret < 0) |
410 | return ret; | |
411 | ||
412 | return sprintf(buf, "%i\n", ret); | |
413 | } | |
414 | ||
415 | static ssize_t | |
c9cbf135 | 416 | nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a, |
11b7d895 MP |
417 | const char *buf, size_t count) |
418 | { | |
419 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
420 | struct nouveau_drm *drm = nouveau_drm(dev); |
421 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
11b7d895 MP |
422 | int ret = -ENODEV; |
423 | long value; | |
424 | ||
ddb20055 | 425 | if (kstrtol(buf, 10, &value) == -EINVAL) |
11b7d895 MP |
426 | return -EINVAL; |
427 | ||
aa1b9b48 | 428 | ret = therm->fan_set(therm, value); |
11b7d895 MP |
429 | if (ret) |
430 | return ret; | |
431 | ||
432 | return count; | |
433 | } | |
434 | ||
c9cbf135 MP |
435 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, |
436 | nouveau_hwmon_get_pwm1, | |
437 | nouveau_hwmon_set_pwm1, 0); | |
11b7d895 MP |
438 | |
439 | static ssize_t | |
c9cbf135 | 440 | nouveau_hwmon_get_pwm1_min(struct device *d, |
11b7d895 MP |
441 | struct device_attribute *a, char *buf) |
442 | { | |
443 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
444 | struct nouveau_drm *drm = nouveau_drm(dev); |
445 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
446 | int ret; | |
447 | ||
448 | ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY); | |
449 | if (ret < 0) | |
450 | return ret; | |
11b7d895 | 451 | |
aa1b9b48 | 452 | return sprintf(buf, "%i\n", ret); |
11b7d895 MP |
453 | } |
454 | ||
455 | static ssize_t | |
c9cbf135 | 456 | nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, |
11b7d895 MP |
457 | const char *buf, size_t count) |
458 | { | |
459 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
460 | struct nouveau_drm *drm = nouveau_drm(dev); |
461 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
11b7d895 | 462 | long value; |
aa1b9b48 | 463 | int ret; |
11b7d895 | 464 | |
ddb20055 | 465 | if (kstrtol(buf, 10, &value) == -EINVAL) |
11b7d895 MP |
466 | return -EINVAL; |
467 | ||
aa1b9b48 MP |
468 | ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY, value); |
469 | if (ret < 0) | |
470 | return ret; | |
11b7d895 MP |
471 | |
472 | return count; | |
473 | } | |
474 | ||
c9cbf135 MP |
475 | static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR, |
476 | nouveau_hwmon_get_pwm1_min, | |
477 | nouveau_hwmon_set_pwm1_min, 0); | |
11b7d895 MP |
478 | |
479 | static ssize_t | |
c9cbf135 | 480 | nouveau_hwmon_get_pwm1_max(struct device *d, |
11b7d895 MP |
481 | struct device_attribute *a, char *buf) |
482 | { | |
483 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
484 | struct nouveau_drm *drm = nouveau_drm(dev); |
485 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
486 | int ret; | |
11b7d895 | 487 | |
aa1b9b48 MP |
488 | ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY); |
489 | if (ret < 0) | |
490 | return ret; | |
11b7d895 | 491 | |
aa1b9b48 | 492 | return sprintf(buf, "%i\n", ret); |
11b7d895 MP |
493 | } |
494 | ||
495 | static ssize_t | |
c9cbf135 | 496 | nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, |
11b7d895 MP |
497 | const char *buf, size_t count) |
498 | { | |
499 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
500 | struct nouveau_drm *drm = nouveau_drm(dev); |
501 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
11b7d895 | 502 | long value; |
aa1b9b48 | 503 | int ret; |
11b7d895 | 504 | |
ddb20055 | 505 | if (kstrtol(buf, 10, &value) == -EINVAL) |
11b7d895 MP |
506 | return -EINVAL; |
507 | ||
aa1b9b48 MP |
508 | ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY, value); |
509 | if (ret < 0) | |
510 | return ret; | |
11b7d895 MP |
511 | |
512 | return count; | |
513 | } | |
514 | ||
c9cbf135 MP |
515 | static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, |
516 | nouveau_hwmon_get_pwm1_max, | |
517 | nouveau_hwmon_set_pwm1_max, 0); | |
11b7d895 | 518 | |
804ca90f MP |
519 | static struct attribute *hwmon_default_attributes[] = { |
520 | &sensor_dev_attr_name.dev_attr.attr, | |
521 | &sensor_dev_attr_update_rate.dev_attr.attr, | |
522 | NULL | |
523 | }; | |
524 | static struct attribute *hwmon_temp_attributes[] = { | |
34e9d85a | 525 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
12e32896 MP |
526 | &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, |
527 | &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, | |
528 | &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, | |
34e9d85a | 529 | &sensor_dev_attr_temp1_max.dev_attr.attr, |
12e32896 | 530 | &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, |
34e9d85a | 531 | &sensor_dev_attr_temp1_crit.dev_attr.attr, |
12e32896 MP |
532 | &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, |
533 | &sensor_dev_attr_temp1_emergency.dev_attr.attr, | |
534 | &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, | |
34e9d85a MP |
535 | NULL |
536 | }; | |
11b7d895 | 537 | static struct attribute *hwmon_fan_rpm_attributes[] = { |
b2c36312 | 538 | &sensor_dev_attr_fan1_input.dev_attr.attr, |
11b7d895 MP |
539 | NULL |
540 | }; | |
541 | static struct attribute *hwmon_pwm_fan_attributes[] = { | |
2f951a5d | 542 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, |
c9cbf135 MP |
543 | &sensor_dev_attr_pwm1.dev_attr.attr, |
544 | &sensor_dev_attr_pwm1_min.dev_attr.attr, | |
545 | &sensor_dev_attr_pwm1_max.dev_attr.attr, | |
11b7d895 MP |
546 | NULL |
547 | }; | |
34e9d85a | 548 | |
804ca90f MP |
549 | static const struct attribute_group hwmon_default_attrgroup = { |
550 | .attrs = hwmon_default_attributes, | |
551 | }; | |
552 | static const struct attribute_group hwmon_temp_attrgroup = { | |
553 | .attrs = hwmon_temp_attributes, | |
34e9d85a | 554 | }; |
11b7d895 MP |
555 | static const struct attribute_group hwmon_fan_rpm_attrgroup = { |
556 | .attrs = hwmon_fan_rpm_attributes, | |
557 | }; | |
558 | static const struct attribute_group hwmon_pwm_fan_attrgroup = { | |
559 | .attrs = hwmon_pwm_fan_attributes, | |
560 | }; | |
b54262f3 | 561 | #endif |
34e9d85a | 562 | |
b9ed919f | 563 | int |
34e9d85a MP |
564 | nouveau_hwmon_init(struct drm_device *dev) |
565 | { | |
095f979a | 566 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
57cdf82c GR |
567 | struct nouveau_drm *drm = nouveau_drm(dev); |
568 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
b9ed919f | 569 | struct nouveau_hwmon *hwmon; |
34e9d85a | 570 | struct device *hwmon_dev; |
11b7d895 | 571 | int ret = 0; |
34e9d85a | 572 | |
b9ed919f BS |
573 | hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL); |
574 | if (!hwmon) | |
575 | return -ENOMEM; | |
576 | hwmon->dev = dev; | |
577 | ||
a5f5af86 | 578 | if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set) |
8155cac4 | 579 | return -ENODEV; |
34e9d85a MP |
580 | |
581 | hwmon_dev = hwmon_device_register(&dev->pdev->dev); | |
582 | if (IS_ERR(hwmon_dev)) { | |
583 | ret = PTR_ERR(hwmon_dev); | |
77145f1c | 584 | NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret); |
34e9d85a MP |
585 | return ret; |
586 | } | |
587 | dev_set_drvdata(hwmon_dev, dev); | |
11b7d895 | 588 | |
804ca90f MP |
589 | /* set the default attributes */ |
590 | ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup); | |
aa34efed DC |
591 | if (ret) |
592 | goto error; | |
11b7d895 | 593 | |
804ca90f MP |
594 | /* if the card has a working thermal sensor */ |
595 | if (therm->temp_get(therm) >= 0) { | |
596 | ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); | |
aa34efed DC |
597 | if (ret) |
598 | goto error; | |
804ca90f MP |
599 | } |
600 | ||
11b7d895 MP |
601 | /* if the card has a pwm fan */ |
602 | /*XXX: incorrect, need better detection for this, some boards have | |
603 | * the gpio entries for pwm fan control even when there's no | |
604 | * actual fan connected to it... therm table? */ | |
aa1b9b48 | 605 | if (therm->fan_get && therm->fan_get(therm) >= 0) { |
5e90a88c | 606 | ret = sysfs_create_group(&hwmon_dev->kobj, |
11b7d895 MP |
607 | &hwmon_pwm_fan_attrgroup); |
608 | if (ret) | |
609 | goto error; | |
610 | } | |
611 | ||
612 | /* if the card can read the fan rpm */ | |
aa1b9b48 | 613 | if (therm->fan_sense(therm) >= 0) { |
5e90a88c | 614 | ret = sysfs_create_group(&hwmon_dev->kobj, |
11b7d895 MP |
615 | &hwmon_fan_rpm_attrgroup); |
616 | if (ret) | |
617 | goto error; | |
34e9d85a MP |
618 | } |
619 | ||
b9ed919f | 620 | hwmon->hwmon = hwmon_dev; |
11b7d895 | 621 | |
34e9d85a | 622 | return 0; |
11b7d895 MP |
623 | |
624 | error: | |
77145f1c | 625 | NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret); |
11b7d895 | 626 | hwmon_device_unregister(hwmon_dev); |
b9ed919f | 627 | hwmon->hwmon = NULL; |
11b7d895 MP |
628 | return ret; |
629 | #else | |
11b7d895 MP |
630 | return 0; |
631 | #endif | |
34e9d85a MP |
632 | } |
633 | ||
b9ed919f | 634 | void |
34e9d85a MP |
635 | nouveau_hwmon_fini(struct drm_device *dev) |
636 | { | |
658e86ee | 637 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
b9ed919f | 638 | struct nouveau_hwmon *hwmon = nouveau_hwmon(dev); |
34e9d85a | 639 | |
b9ed919f BS |
640 | if (hwmon->hwmon) { |
641 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup); | |
642 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup); | |
643 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup); | |
644 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup); | |
11b7d895 | 645 | |
b9ed919f | 646 | hwmon_device_unregister(hwmon->hwmon); |
34e9d85a | 647 | } |
6032649d | 648 | |
b9ed919f BS |
649 | nouveau_drm(dev)->hwmon = NULL; |
650 | kfree(hwmon); | |
6032649d | 651 | #endif |
64f1c11a | 652 | } |