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