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