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 | ||
386 | if (strict_strtol(buf, 10, &value) == -EINVAL) | |
387 | return -EINVAL; | |
11b7d895 | 388 | |
2f951a5d MP |
389 | ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MODE, value); |
390 | if (ret) | |
391 | return ret; | |
392 | else | |
393 | return count; | |
11b7d895 | 394 | } |
2f951a5d MP |
395 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, |
396 | nouveau_hwmon_get_pwm1_enable, | |
397 | nouveau_hwmon_set_pwm1_enable, 0); | |
11b7d895 MP |
398 | |
399 | static ssize_t | |
c9cbf135 | 400 | nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf) |
11b7d895 MP |
401 | { |
402 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
403 | struct nouveau_drm *drm = nouveau_drm(dev); |
404 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
a175094c | 405 | int ret; |
11b7d895 | 406 | |
aa1b9b48 | 407 | ret = therm->fan_get(therm); |
11b7d895 MP |
408 | if (ret < 0) |
409 | return ret; | |
410 | ||
411 | return sprintf(buf, "%i\n", ret); | |
412 | } | |
413 | ||
414 | static ssize_t | |
c9cbf135 | 415 | nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a, |
11b7d895 MP |
416 | const char *buf, size_t count) |
417 | { | |
418 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
419 | struct nouveau_drm *drm = nouveau_drm(dev); |
420 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
11b7d895 MP |
421 | int ret = -ENODEV; |
422 | long value; | |
423 | ||
ddb20055 | 424 | if (kstrtol(buf, 10, &value) == -EINVAL) |
11b7d895 MP |
425 | return -EINVAL; |
426 | ||
aa1b9b48 | 427 | ret = therm->fan_set(therm, value); |
11b7d895 MP |
428 | if (ret) |
429 | return ret; | |
430 | ||
431 | return count; | |
432 | } | |
433 | ||
c9cbf135 MP |
434 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, |
435 | nouveau_hwmon_get_pwm1, | |
436 | nouveau_hwmon_set_pwm1, 0); | |
11b7d895 MP |
437 | |
438 | static ssize_t | |
c9cbf135 | 439 | nouveau_hwmon_get_pwm1_min(struct device *d, |
11b7d895 MP |
440 | struct device_attribute *a, char *buf) |
441 | { | |
442 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
443 | struct nouveau_drm *drm = nouveau_drm(dev); |
444 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
445 | int ret; | |
446 | ||
447 | ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY); | |
448 | if (ret < 0) | |
449 | return ret; | |
11b7d895 | 450 | |
aa1b9b48 | 451 | return sprintf(buf, "%i\n", ret); |
11b7d895 MP |
452 | } |
453 | ||
454 | static ssize_t | |
c9cbf135 | 455 | nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, |
11b7d895 MP |
456 | const char *buf, size_t count) |
457 | { | |
458 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
459 | struct nouveau_drm *drm = nouveau_drm(dev); |
460 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
11b7d895 | 461 | long value; |
aa1b9b48 | 462 | int ret; |
11b7d895 | 463 | |
ddb20055 | 464 | if (kstrtol(buf, 10, &value) == -EINVAL) |
11b7d895 MP |
465 | return -EINVAL; |
466 | ||
aa1b9b48 MP |
467 | ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY, value); |
468 | if (ret < 0) | |
469 | return ret; | |
11b7d895 MP |
470 | |
471 | return count; | |
472 | } | |
473 | ||
c9cbf135 MP |
474 | static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR, |
475 | nouveau_hwmon_get_pwm1_min, | |
476 | nouveau_hwmon_set_pwm1_min, 0); | |
11b7d895 MP |
477 | |
478 | static ssize_t | |
c9cbf135 | 479 | nouveau_hwmon_get_pwm1_max(struct device *d, |
11b7d895 MP |
480 | struct device_attribute *a, char *buf) |
481 | { | |
482 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
483 | struct nouveau_drm *drm = nouveau_drm(dev); |
484 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
485 | int ret; | |
11b7d895 | 486 | |
aa1b9b48 MP |
487 | ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY); |
488 | if (ret < 0) | |
489 | return ret; | |
11b7d895 | 490 | |
aa1b9b48 | 491 | return sprintf(buf, "%i\n", ret); |
11b7d895 MP |
492 | } |
493 | ||
494 | static ssize_t | |
c9cbf135 | 495 | nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, |
11b7d895 MP |
496 | const char *buf, size_t count) |
497 | { | |
498 | struct drm_device *dev = dev_get_drvdata(d); | |
aa1b9b48 MP |
499 | struct nouveau_drm *drm = nouveau_drm(dev); |
500 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
11b7d895 | 501 | long value; |
aa1b9b48 | 502 | int ret; |
11b7d895 | 503 | |
ddb20055 | 504 | if (kstrtol(buf, 10, &value) == -EINVAL) |
11b7d895 MP |
505 | return -EINVAL; |
506 | ||
aa1b9b48 MP |
507 | ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY, value); |
508 | if (ret < 0) | |
509 | return ret; | |
11b7d895 MP |
510 | |
511 | return count; | |
512 | } | |
513 | ||
c9cbf135 MP |
514 | static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, |
515 | nouveau_hwmon_get_pwm1_max, | |
516 | nouveau_hwmon_set_pwm1_max, 0); | |
11b7d895 | 517 | |
804ca90f MP |
518 | static struct attribute *hwmon_default_attributes[] = { |
519 | &sensor_dev_attr_name.dev_attr.attr, | |
520 | &sensor_dev_attr_update_rate.dev_attr.attr, | |
521 | NULL | |
522 | }; | |
523 | static struct attribute *hwmon_temp_attributes[] = { | |
34e9d85a | 524 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
12e32896 MP |
525 | &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, |
526 | &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, | |
527 | &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, | |
34e9d85a | 528 | &sensor_dev_attr_temp1_max.dev_attr.attr, |
12e32896 | 529 | &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, |
34e9d85a | 530 | &sensor_dev_attr_temp1_crit.dev_attr.attr, |
12e32896 MP |
531 | &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, |
532 | &sensor_dev_attr_temp1_emergency.dev_attr.attr, | |
533 | &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, | |
34e9d85a MP |
534 | NULL |
535 | }; | |
11b7d895 | 536 | static struct attribute *hwmon_fan_rpm_attributes[] = { |
b2c36312 | 537 | &sensor_dev_attr_fan1_input.dev_attr.attr, |
11b7d895 MP |
538 | NULL |
539 | }; | |
540 | static struct attribute *hwmon_pwm_fan_attributes[] = { | |
2f951a5d | 541 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, |
c9cbf135 MP |
542 | &sensor_dev_attr_pwm1.dev_attr.attr, |
543 | &sensor_dev_attr_pwm1_min.dev_attr.attr, | |
544 | &sensor_dev_attr_pwm1_max.dev_attr.attr, | |
11b7d895 MP |
545 | NULL |
546 | }; | |
34e9d85a | 547 | |
804ca90f MP |
548 | static const struct attribute_group hwmon_default_attrgroup = { |
549 | .attrs = hwmon_default_attributes, | |
550 | }; | |
551 | static const struct attribute_group hwmon_temp_attrgroup = { | |
552 | .attrs = hwmon_temp_attributes, | |
34e9d85a | 553 | }; |
11b7d895 MP |
554 | static const struct attribute_group hwmon_fan_rpm_attrgroup = { |
555 | .attrs = hwmon_fan_rpm_attributes, | |
556 | }; | |
557 | static const struct attribute_group hwmon_pwm_fan_attrgroup = { | |
558 | .attrs = hwmon_pwm_fan_attributes, | |
559 | }; | |
b54262f3 | 560 | #endif |
34e9d85a | 561 | |
b9ed919f | 562 | int |
34e9d85a MP |
563 | nouveau_hwmon_init(struct drm_device *dev) |
564 | { | |
095f979a | 565 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
57cdf82c GR |
566 | struct nouveau_drm *drm = nouveau_drm(dev); |
567 | struct nouveau_therm *therm = nouveau_therm(drm->device); | |
b9ed919f | 568 | struct nouveau_hwmon *hwmon; |
34e9d85a | 569 | struct device *hwmon_dev; |
11b7d895 | 570 | int ret = 0; |
34e9d85a | 571 | |
b9ed919f BS |
572 | hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL); |
573 | if (!hwmon) | |
574 | return -ENOMEM; | |
575 | hwmon->dev = dev; | |
576 | ||
a5f5af86 | 577 | if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set) |
8155cac4 | 578 | return -ENODEV; |
34e9d85a MP |
579 | |
580 | hwmon_dev = hwmon_device_register(&dev->pdev->dev); | |
581 | if (IS_ERR(hwmon_dev)) { | |
582 | ret = PTR_ERR(hwmon_dev); | |
77145f1c | 583 | NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret); |
34e9d85a MP |
584 | return ret; |
585 | } | |
586 | dev_set_drvdata(hwmon_dev, dev); | |
11b7d895 | 587 | |
804ca90f MP |
588 | /* set the default attributes */ |
589 | ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup); | |
34e9d85a | 590 | if (ret) { |
11b7d895 MP |
591 | if (ret) |
592 | goto error; | |
593 | } | |
594 | ||
804ca90f MP |
595 | /* if the card has a working thermal sensor */ |
596 | if (therm->temp_get(therm) >= 0) { | |
597 | ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); | |
598 | if (ret) { | |
599 | if (ret) | |
600 | goto error; | |
601 | } | |
602 | } | |
603 | ||
11b7d895 MP |
604 | /* if the card has a pwm fan */ |
605 | /*XXX: incorrect, need better detection for this, some boards have | |
606 | * the gpio entries for pwm fan control even when there's no | |
607 | * actual fan connected to it... therm table? */ | |
aa1b9b48 | 608 | if (therm->fan_get && therm->fan_get(therm) >= 0) { |
5e90a88c | 609 | ret = sysfs_create_group(&hwmon_dev->kobj, |
11b7d895 MP |
610 | &hwmon_pwm_fan_attrgroup); |
611 | if (ret) | |
612 | goto error; | |
613 | } | |
614 | ||
615 | /* if the card can read the fan rpm */ | |
aa1b9b48 | 616 | if (therm->fan_sense(therm) >= 0) { |
5e90a88c | 617 | ret = sysfs_create_group(&hwmon_dev->kobj, |
11b7d895 MP |
618 | &hwmon_fan_rpm_attrgroup); |
619 | if (ret) | |
620 | goto error; | |
34e9d85a MP |
621 | } |
622 | ||
b9ed919f | 623 | hwmon->hwmon = hwmon_dev; |
11b7d895 | 624 | |
34e9d85a | 625 | return 0; |
11b7d895 MP |
626 | |
627 | error: | |
77145f1c | 628 | NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret); |
11b7d895 | 629 | hwmon_device_unregister(hwmon_dev); |
b9ed919f | 630 | hwmon->hwmon = NULL; |
11b7d895 MP |
631 | return ret; |
632 | #else | |
11b7d895 MP |
633 | return 0; |
634 | #endif | |
34e9d85a MP |
635 | } |
636 | ||
b9ed919f | 637 | void |
34e9d85a MP |
638 | nouveau_hwmon_fini(struct drm_device *dev) |
639 | { | |
658e86ee | 640 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
b9ed919f | 641 | struct nouveau_hwmon *hwmon = nouveau_hwmon(dev); |
34e9d85a | 642 | |
b9ed919f BS |
643 | if (hwmon->hwmon) { |
644 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup); | |
645 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup); | |
646 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup); | |
647 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup); | |
11b7d895 | 648 | |
b9ed919f | 649 | hwmon_device_unregister(hwmon->hwmon); |
34e9d85a | 650 | } |
6032649d | 651 | |
b9ed919f BS |
652 | nouveau_drm(dev)->hwmon = NULL; |
653 | kfree(hwmon); | |
6032649d | 654 | #endif |
64f1c11a | 655 | } |