Linux 6.17-rc6
[linux-2.6-block.git] / drivers / thermal / thermal_sysfs.c
CommitLineData
7e3c0381 1// SPDX-License-Identifier: GPL-2.0
a369ee88
EV
2/*
3 * thermal.c - sysfs interface of thermal devices
4 *
5 * Copyright (C) 2016 Eduardo Valentin <edubezval@gmail.com>
6 *
7 * Highly based on original thermal_core.c
8 * Copyright (C) 2008 Intel Corp
9 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
10 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
a369ee88
EV
11 */
12
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
afd84fb1 15#include <linux/container_of.h>
a369ee88
EV
16#include <linux/sysfs.h>
17#include <linux/device.h>
18#include <linux/err.h>
19#include <linux/slab.h>
20#include <linux/string.h>
8ea22951 21#include <linux/jiffies.h>
a369ee88
EV
22
23#include "thermal_core.h"
24
25/* sys I/F for thermal zone */
26
27static ssize_t
28type_show(struct device *dev, struct device_attribute *attr, char *buf)
29{
30 struct thermal_zone_device *tz = to_thermal_zone(dev);
31
32 return sprintf(buf, "%s\n", tz->type);
33}
34
35static ssize_t
36temp_show(struct device *dev, struct device_attribute *attr, char *buf)
37{
38 struct thermal_zone_device *tz = to_thermal_zone(dev);
39 int temperature, ret;
40
41 ret = thermal_zone_get_temp(tz, &temperature);
42
1a4aabc2
HTY
43 if (!ret)
44 return sprintf(buf, "%d\n", temperature);
a369ee88 45
1a4aabc2
HTY
46 if (ret == -EAGAIN)
47 return -ENODATA;
48
49 return ret;
a369ee88
EV
50}
51
52static ssize_t
53mode_show(struct device *dev, struct device_attribute *attr, char *buf)
54{
55 struct thermal_zone_device *tz = to_thermal_zone(dev);
a930da9b 56
cba00d16 57 guard(thermal_zone)(tz);
a369ee88 58
cba00d16
RW
59 if (tz->mode == THERMAL_DEVICE_ENABLED)
60 return sprintf(buf, "enabled\n");
61
62 return sprintf(buf, "disabled\n");
a369ee88
EV
63}
64
65static ssize_t
66mode_store(struct device *dev, struct device_attribute *attr,
67 const char *buf, size_t count)
68{
69 struct thermal_zone_device *tz = to_thermal_zone(dev);
70 int result;
71
a369ee88 72 if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
7f4957be 73 result = thermal_zone_device_enable(tz);
a369ee88 74 else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
7f4957be 75 result = thermal_zone_device_disable(tz);
a369ee88
EV
76 else
77 result = -EINVAL;
78
79 if (result)
80 return result;
81
82 return count;
83}
84
afd84fb1
RW
85#define thermal_trip_of_attr(_ptr_, _attr_) \
86 ({ \
87 struct thermal_trip_desc *td; \
88 \
89 td = container_of(_ptr_, struct thermal_trip_desc, \
90 trip_attrs._attr_.attr); \
91 &td->trip; \
92 })
93
a369ee88
EV
94static ssize_t
95trip_point_type_show(struct device *dev, struct device_attribute *attr,
96 char *buf)
97{
afd84fb1 98 struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
a369ee88 99
afd84fb1 100 return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type));
a369ee88
EV
101}
102
103static ssize_t
104trip_point_temp_store(struct device *dev, struct device_attribute *attr,
105 const char *buf, size_t count)
106{
afd84fb1 107 struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
a369ee88 108 struct thermal_zone_device *tz = to_thermal_zone(dev);
cba00d16 109 int temp;
be0a3600 110
cba00d16 111 if (kstrtoint(buf, 10, &temp))
be0a3600 112 return -EINVAL;
a369ee88 113
cba00d16 114 guard(thermal_zone)(tz);
05eeee2b 115
874b6476 116 if (temp == trip->temperature)
cba00d16 117 return count;
874b6476
RW
118
119 /* Arrange the condition to avoid integer overflows. */
120 if (temp != THERMAL_TEMP_INVALID &&
cba00d16
RW
121 temp <= trip->hysteresis + THERMAL_TEMP_INVALID)
122 return -EINVAL;
be0a3600 123
874b6476 124 if (tz->ops.set_trip_temp) {
cba00d16
RW
125 int ret;
126
874b6476
RW
127 ret = tz->ops.set_trip_temp(tz, trip, temp);
128 if (ret)
cba00d16 129 return ret;
be0a3600 130 }
05eeee2b 131
874b6476
RW
132 thermal_zone_set_trip_temp(tz, trip, temp);
133
134 __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
135
cba00d16 136 return count;
a369ee88
EV
137}
138
139static ssize_t
140trip_point_temp_show(struct device *dev, struct device_attribute *attr,
141 char *buf)
142{
afd84fb1 143 struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
a369ee88 144
afd84fb1 145 return sprintf(buf, "%d\n", READ_ONCE(trip->temperature));
a369ee88
EV
146}
147
148static ssize_t
149trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
150 const char *buf, size_t count)
151{
afd84fb1 152 struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
a369ee88 153 struct thermal_zone_device *tz = to_thermal_zone(dev);
cba00d16 154 int hyst;
be0a3600 155
cba00d16 156 if (kstrtoint(buf, 10, &hyst) || hyst < 0)
be0a3600 157 return -EINVAL;
a369ee88 158
cba00d16 159 guard(thermal_zone)(tz);
05eeee2b 160
874b6476 161 if (hyst == trip->hysteresis)
cba00d16 162 return count;
874b6476
RW
163
164 /*
165 * Allow the hysteresis to be updated when the temperature is invalid
166 * to allow user space to avoid having to adjust hysteresis after a
167 * valid temperature has been set, but in that case just change the
168 * value and do nothing else.
169 */
170 if (trip->temperature == THERMAL_TEMP_INVALID) {
171 WRITE_ONCE(trip->hysteresis, hyst);
cba00d16 172 return count;
874b6476 173 }
be0a3600 174
cba00d16
RW
175 if (trip->temperature - hyst <= THERMAL_TEMP_INVALID)
176 return -EINVAL;
ea310567 177
874b6476
RW
178 thermal_zone_set_trip_hyst(tz, trip, hyst);
179
180 __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
181
cba00d16 182 return count;
a369ee88
EV
183}
184
185static ssize_t
186trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
187 char *buf)
188{
afd84fb1 189 struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
a369ee88 190
afd84fb1 191 return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis));
a369ee88
EV
192}
193
a369ee88
EV
194static ssize_t
195policy_store(struct device *dev, struct device_attribute *attr,
196 const char *buf, size_t count)
197{
198 struct thermal_zone_device *tz = to_thermal_zone(dev);
199 char name[THERMAL_NAME_LENGTH];
200 int ret;
201
202 snprintf(name, sizeof(name), "%s", buf);
203
204 ret = thermal_zone_device_set_policy(tz, name);
205 if (!ret)
206 ret = count;
207
208 return ret;
209}
210
211static ssize_t
212policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
213{
214 struct thermal_zone_device *tz = to_thermal_zone(dev);
215
216 return sprintf(buf, "%s\n", tz->governor->name);
217}
218
219static ssize_t
220available_policies_show(struct device *dev, struct device_attribute *devattr,
221 char *buf)
222{
223 return thermal_build_list_of_policies(buf);
224}
225
698db4fd 226#if (IS_ENABLED(CONFIG_THERMAL_EMULATION))
a369ee88
EV
227static ssize_t
228emul_temp_store(struct device *dev, struct device_attribute *attr,
229 const char *buf, size_t count)
230{
231 struct thermal_zone_device *tz = to_thermal_zone(dev);
a369ee88
EV
232 int temperature;
233
234 if (kstrtoint(buf, 10, &temperature))
235 return -EINVAL;
236
cba00d16 237 guard(thermal_zone)(tz);
05eeee2b 238
cba00d16
RW
239 if (tz->ops.set_emul_temp) {
240 int ret;
a369ee88 241
cba00d16
RW
242 ret = tz->ops.set_emul_temp(tz, temperature);
243 if (ret)
244 return ret;
245 } else {
246 tz->emul_temperature = temperature;
247 }
05eeee2b 248
cba00d16 249 __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
a369ee88 250
cba00d16 251 return count;
a369ee88 252}
6cbaefb4 253static DEVICE_ATTR_WO(emul_temp);
698db4fd 254#endif
a369ee88
EV
255
256static ssize_t
257sustainable_power_show(struct device *dev, struct device_attribute *devattr,
258 char *buf)
259{
260 struct thermal_zone_device *tz = to_thermal_zone(dev);
261
262 if (tz->tzp)
263 return sprintf(buf, "%u\n", tz->tzp->sustainable_power);
264 else
265 return -EIO;
266}
267
268static ssize_t
269sustainable_power_store(struct device *dev, struct device_attribute *devattr,
270 const char *buf, size_t count)
271{
272 struct thermal_zone_device *tz = to_thermal_zone(dev);
273 u32 sustainable_power;
274
275 if (!tz->tzp)
276 return -EIO;
277
278 if (kstrtou32(buf, 10, &sustainable_power))
279 return -EINVAL;
280
281 tz->tzp->sustainable_power = sustainable_power;
282
283 return count;
284}
285
286#define create_s32_tzp_attr(name) \
287 static ssize_t \
288 name##_show(struct device *dev, struct device_attribute *devattr, \
289 char *buf) \
290 { \
291 struct thermal_zone_device *tz = to_thermal_zone(dev); \
292 \
293 if (tz->tzp) \
294 return sprintf(buf, "%d\n", tz->tzp->name); \
295 else \
296 return -EIO; \
297 } \
298 \
299 static ssize_t \
300 name##_store(struct device *dev, struct device_attribute *devattr, \
301 const char *buf, size_t count) \
302 { \
303 struct thermal_zone_device *tz = to_thermal_zone(dev); \
304 s32 value; \
305 \
306 if (!tz->tzp) \
307 return -EIO; \
308 \
309 if (kstrtos32(buf, 10, &value)) \
310 return -EINVAL; \
311 \
312 tz->tzp->name = value; \
313 \
314 return count; \
315 } \
e76a4386 316 static DEVICE_ATTR_RW(name)
a369ee88
EV
317
318create_s32_tzp_attr(k_po);
319create_s32_tzp_attr(k_pu);
320create_s32_tzp_attr(k_i);
321create_s32_tzp_attr(k_d);
322create_s32_tzp_attr(integral_cutoff);
323create_s32_tzp_attr(slope);
324create_s32_tzp_attr(offset);
325#undef create_s32_tzp_attr
326
327/*
328 * These are thermal zone device attributes that will always be present.
329 * All the attributes created for tzp (create_s32_tzp_attr) also are always
330 * present on the sysfs interface.
331 */
c828a892
JP
332static DEVICE_ATTR_RO(type);
333static DEVICE_ATTR_RO(temp);
b6b996b6 334static DEVICE_ATTR_RW(policy);
c828a892 335static DEVICE_ATTR_RO(available_policies);
b6b996b6 336static DEVICE_ATTR_RW(sustainable_power);
a369ee88
EV
337
338/* These thermal zone device attributes are created based on conditions */
b6b996b6 339static DEVICE_ATTR_RW(mode);
a369ee88
EV
340
341/* These attributes are unconditionally added to a thermal zone */
342static struct attribute *thermal_zone_dev_attrs[] = {
343 &dev_attr_type.attr,
344 &dev_attr_temp.attr,
345#if (IS_ENABLED(CONFIG_THERMAL_EMULATION))
346 &dev_attr_emul_temp.attr,
347#endif
348 &dev_attr_policy.attr,
349 &dev_attr_available_policies.attr,
350 &dev_attr_sustainable_power.attr,
351 &dev_attr_k_po.attr,
352 &dev_attr_k_pu.attr,
353 &dev_attr_k_i.attr,
354 &dev_attr_k_d.attr,
355 &dev_attr_integral_cutoff.attr,
356 &dev_attr_slope.attr,
357 &dev_attr_offset.attr,
358 NULL,
359};
360
f74bed6a 361static const struct attribute_group thermal_zone_attribute_group = {
a369ee88
EV
362 .attrs = thermal_zone_dev_attrs,
363};
364
a369ee88
EV
365static struct attribute *thermal_zone_mode_attrs[] = {
366 &dev_attr_mode.attr,
367 NULL,
368};
369
f74bed6a 370static const struct attribute_group thermal_zone_mode_attribute_group = {
a369ee88 371 .attrs = thermal_zone_mode_attrs,
a369ee88
EV
372};
373
a369ee88
EV
374static const struct attribute_group *thermal_zone_attribute_groups[] = {
375 &thermal_zone_attribute_group,
376 &thermal_zone_mode_attribute_group,
a369ee88
EV
377 /* This is not NULL terminated as we create the group dynamically */
378};
379
380/**
381 * create_trip_attrs() - create attributes for trip points
382 * @tz: the thermal zone device
a369ee88
EV
383 *
384 * helper function to instantiate sysfs entries for every trip
385 * point and its properties of a struct thermal_zone_device.
386 *
387 * Return: 0 on success, the proper error value otherwise.
388 */
5340f764 389static int create_trip_attrs(struct thermal_zone_device *tz)
a369ee88 390{
66b26330 391 struct thermal_trip_desc *td;
a369ee88 392 struct attribute **attrs;
66b26330 393 int i;
a369ee88 394
e5bfcd30 395 attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL);
66b26330 396 if (!attrs)
a369ee88 397 return -ENOMEM;
a369ee88 398
66b26330 399 i = 0;
daeeb032 400 for_each_trip_desc(tz, td) {
66b26330 401 struct thermal_trip_attrs *trip_attrs = &td->trip_attrs;
5340f764 402
a369ee88 403 /* create trip type attribute */
66b26330
RW
404 snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH,
405 "trip_point_%d_type", i);
a369ee88 406
66b26330
RW
407 sysfs_attr_init(&trip_attrs->type.attr.attr);
408 trip_attrs->type.attr.attr.name = trip_attrs->type.name;
409 trip_attrs->type.attr.attr.mode = S_IRUGO;
410 trip_attrs->type.attr.show = trip_point_type_show;
411 attrs[i] = &trip_attrs->type.attr.attr;
a369ee88
EV
412
413 /* create trip temp attribute */
66b26330
RW
414 snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH,
415 "trip_point_%d_temp", i);
416
417 sysfs_attr_init(&trip_attrs->temp.attr.attr);
418 trip_attrs->temp.attr.attr.name = trip_attrs->temp.name;
419 trip_attrs->temp.attr.attr.mode = S_IRUGO;
420 trip_attrs->temp.attr.show = trip_point_temp_show;
daeeb032 421 if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) {
66b26330
RW
422 trip_attrs->temp.attr.attr.mode |= S_IWUSR;
423 trip_attrs->temp.attr.store = trip_point_temp_store;
a369ee88 424 }
66b26330 425 attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr;
a369ee88 426
66b26330
RW
427 snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH,
428 "trip_point_%d_hyst", i);
a369ee88 429
66b26330
RW
430 sysfs_attr_init(&trip_attrs->hyst.attr.attr);
431 trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name;
432 trip_attrs->hyst.attr.attr.mode = S_IRUGO;
433 trip_attrs->hyst.attr.show = trip_point_hyst_show;
daeeb032 434 if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) {
66b26330
RW
435 trip_attrs->hyst.attr.attr.mode |= S_IWUSR;
436 trip_attrs->hyst.attr.store = trip_point_hyst_store;
a369ee88 437 }
66b26330
RW
438 attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr;
439 i++;
a369ee88 440 }
e5bfcd30 441 attrs[tz->num_trips * 3] = NULL;
a369ee88
EV
442
443 tz->trips_attribute_group.attrs = attrs;
444
445 return 0;
446}
447
32fa5ba3
CJ
448/**
449 * destroy_trip_attrs() - destroy attributes for trip points
450 * @tz: the thermal zone device
451 *
452 * helper function to free resources allocated by create_trip_attrs()
453 */
454static void destroy_trip_attrs(struct thermal_zone_device *tz)
455{
66b26330
RW
456 if (tz)
457 kfree(tz->trips_attribute_group.attrs);
32fa5ba3
CJ
458}
459
5340f764 460int thermal_zone_create_device_groups(struct thermal_zone_device *tz)
a369ee88
EV
461{
462 const struct attribute_group **groups;
463 int i, size, result;
464
465 /* we need one extra for trips and the NULL to terminate the array */
466 size = ARRAY_SIZE(thermal_zone_attribute_groups) + 2;
467 /* This also takes care of API requirement to be NULL terminated */
468 groups = kcalloc(size, sizeof(*groups), GFP_KERNEL);
469 if (!groups)
470 return -ENOMEM;
471
472 for (i = 0; i < size - 2; i++)
473 groups[i] = thermal_zone_attribute_groups[i];
474
e5bfcd30 475 if (tz->num_trips) {
5340f764 476 result = create_trip_attrs(tz);
a369ee88
EV
477 if (result) {
478 kfree(groups);
479
480 return result;
481 }
482
483 groups[size - 2] = &tz->trips_attribute_group;
484 }
485
486 tz->device.groups = groups;
487
488 return 0;
489}
45cf2ec9 490
32fa5ba3
CJ
491void thermal_zone_destroy_device_groups(struct thermal_zone_device *tz)
492{
493 if (!tz)
494 return;
495
e5bfcd30 496 if (tz->num_trips)
32fa5ba3
CJ
497 destroy_trip_attrs(tz);
498
499 kfree(tz->device.groups);
500}
501
45cf2ec9
EV
502/* sys I/F for cooling device */
503static ssize_t
33e678d4 504cdev_type_show(struct device *dev, struct device_attribute *attr, char *buf)
45cf2ec9
EV
505{
506 struct thermal_cooling_device *cdev = to_cooling_device(dev);
507
508 return sprintf(buf, "%s\n", cdev->type);
509}
510
33e678d4
VK
511static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
512 char *buf)
45cf2ec9
EV
513{
514 struct thermal_cooling_device *cdev = to_cooling_device(dev);
45cf2ec9 515
c408b3d1 516 return sprintf(buf, "%ld\n", cdev->max_state);
45cf2ec9
EV
517}
518
33e678d4
VK
519static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
520 char *buf)
45cf2ec9
EV
521{
522 struct thermal_cooling_device *cdev = to_cooling_device(dev);
523 unsigned long state;
524 int ret;
525
526 ret = cdev->ops->get_cur_state(cdev, &state);
527 if (ret)
528 return ret;
529 return sprintf(buf, "%ld\n", state);
530}
531
532static ssize_t
33e678d4
VK
533cur_state_store(struct device *dev, struct device_attribute *attr,
534 const char *buf, size_t count)
45cf2ec9
EV
535{
536 struct thermal_cooling_device *cdev = to_cooling_device(dev);
537 unsigned long state;
538 int result;
539
540 if (sscanf(buf, "%ld\n", &state) != 1)
541 return -EINVAL;
542
543 if ((long)state < 0)
544 return -EINVAL;
545
c408b3d1
VK
546 /* Requested state should be less than max_state + 1 */
547 if (state > cdev->max_state)
548 return -EINVAL;
549
a5a98a78 550 guard(cooling_dev)(cdev);
68000a0d 551
45cf2ec9 552 result = cdev->ops->set_cur_state(cdev, state);
a5a98a78
RW
553 if (result)
554 return result;
555
556 thermal_cooling_device_stats_update(cdev, state);
68000a0d 557
a5a98a78 558 return count;
45cf2ec9
EV
559}
560
33e678d4
VK
561static struct device_attribute
562dev_attr_cdev_type = __ATTR(type, 0444, cdev_type_show, NULL);
e76a4386
VK
563static DEVICE_ATTR_RO(max_state);
564static DEVICE_ATTR_RW(cur_state);
45cf2ec9
EV
565
566static struct attribute *cooling_device_attrs[] = {
567 &dev_attr_cdev_type.attr,
568 &dev_attr_max_state.attr,
569 &dev_attr_cur_state.attr,
570 NULL,
571};
572
573static const struct attribute_group cooling_device_attr_group = {
574 .attrs = cooling_device_attrs,
575};
576
577static const struct attribute_group *cooling_device_attr_groups[] = {
578 &cooling_device_attr_group,
8ea22951 579 NULL, /* Space allocated for cooling_device_stats_attr_group */
45cf2ec9
EV
580 NULL,
581};
582
8ea22951
VK
583#ifdef CONFIG_THERMAL_STATISTICS
584struct cooling_dev_stats {
585 spinlock_t lock;
586 unsigned int total_trans;
587 unsigned long state;
8ea22951
VK
588 ktime_t last_time;
589 ktime_t *time_in_state;
590 unsigned int *trans_table;
591};
592
593static void update_time_in_state(struct cooling_dev_stats *stats)
594{
595 ktime_t now = ktime_get(), delta;
596
597 delta = ktime_sub(now, stats->last_time);
598 stats->time_in_state[stats->state] =
599 ktime_add(stats->time_in_state[stats->state], delta);
600 stats->last_time = now;
601}
602
603void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
604 unsigned long new_state)
605{
606 struct cooling_dev_stats *stats = cdev->stats;
607
790930f4
RW
608 lockdep_assert_held(&cdev->lock);
609
2046a24a
MMP
610 if (!stats)
611 return;
612
8ea22951
VK
613 spin_lock(&stats->lock);
614
615 if (stats->state == new_state)
616 goto unlock;
617
618 update_time_in_state(stats);
a365105c 619 stats->trans_table[stats->state * (cdev->max_state + 1) + new_state]++;
8ea22951
VK
620 stats->state = new_state;
621 stats->total_trans++;
622
623unlock:
624 spin_unlock(&stats->lock);
625}
626
33e678d4
VK
627static ssize_t total_trans_show(struct device *dev,
628 struct device_attribute *attr, char *buf)
8ea22951
VK
629{
630 struct thermal_cooling_device *cdev = to_cooling_device(dev);
790930f4 631 struct cooling_dev_stats *stats;
a5a98a78 632 int ret;
790930f4 633
a5a98a78 634 guard(cooling_dev)(cdev);
790930f4
RW
635
636 stats = cdev->stats;
637 if (!stats)
a5a98a78 638 return 0;
8ea22951
VK
639
640 spin_lock(&stats->lock);
641 ret = sprintf(buf, "%u\n", stats->total_trans);
642 spin_unlock(&stats->lock);
643
644 return ret;
645}
646
647static ssize_t
33e678d4
VK
648time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
649 char *buf)
8ea22951
VK
650{
651 struct thermal_cooling_device *cdev = to_cooling_device(dev);
790930f4 652 struct cooling_dev_stats *stats;
8ea22951
VK
653 ssize_t len = 0;
654 int i;
655
a5a98a78 656 guard(cooling_dev)(cdev);
790930f4
RW
657
658 stats = cdev->stats;
659 if (!stats)
a5a98a78 660 return 0;
790930f4 661
8ea22951 662 spin_lock(&stats->lock);
790930f4 663
8ea22951
VK
664 update_time_in_state(stats);
665
a365105c 666 for (i = 0; i <= cdev->max_state; i++) {
8ea22951
VK
667 len += sprintf(buf + len, "state%u\t%llu\n", i,
668 ktime_to_ms(stats->time_in_state[i]));
669 }
670 spin_unlock(&stats->lock);
671
672 return len;
673}
674
675static ssize_t
33e678d4
VK
676reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
677 size_t count)
8ea22951
VK
678{
679 struct thermal_cooling_device *cdev = to_cooling_device(dev);
790930f4
RW
680 struct cooling_dev_stats *stats;
681 int i, states;
682
a5a98a78 683 guard(cooling_dev)(cdev);
790930f4
RW
684
685 stats = cdev->stats;
686 if (!stats)
a5a98a78 687 return count;
790930f4
RW
688
689 states = cdev->max_state + 1;
8ea22951
VK
690
691 spin_lock(&stats->lock);
692
693 stats->total_trans = 0;
694 stats->last_time = ktime_get();
695 memset(stats->trans_table, 0,
696 states * states * sizeof(*stats->trans_table));
697
a365105c 698 for (i = 0; i < states; i++)
8ea22951
VK
699 stats->time_in_state[i] = ktime_set(0, 0);
700
701 spin_unlock(&stats->lock);
702
703 return count;
704}
705
33e678d4
VK
706static ssize_t trans_table_show(struct device *dev,
707 struct device_attribute *attr, char *buf)
8ea22951
VK
708{
709 struct thermal_cooling_device *cdev = to_cooling_device(dev);
790930f4 710 struct cooling_dev_stats *stats;
8ea22951
VK
711 ssize_t len = 0;
712 int i, j;
713
a5a98a78 714 guard(cooling_dev)(cdev);
790930f4
RW
715
716 stats = cdev->stats;
a5a98a78
RW
717 if (!stats)
718 return -ENODATA;
790930f4 719
8ea22951
VK
720 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
721 len += snprintf(buf + len, PAGE_SIZE - len, " : ");
a365105c 722 for (i = 0; i <= cdev->max_state; i++) {
8ea22951
VK
723 if (len >= PAGE_SIZE)
724 break;
725 len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
726 }
a5a98a78
RW
727 if (len >= PAGE_SIZE)
728 return PAGE_SIZE;
8ea22951
VK
729
730 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
731
a365105c 732 for (i = 0; i <= cdev->max_state; i++) {
8ea22951
VK
733 if (len >= PAGE_SIZE)
734 break;
735
736 len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
737
a365105c 738 for (j = 0; j <= cdev->max_state; j++) {
8ea22951
VK
739 if (len >= PAGE_SIZE)
740 break;
741 len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
a365105c 742 stats->trans_table[i * (cdev->max_state + 1) + j]);
8ea22951
VK
743 }
744 if (len >= PAGE_SIZE)
745 break;
746 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
747 }
748
749 if (len >= PAGE_SIZE) {
750 pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
790930f4 751 len = -EFBIG;
8ea22951 752 }
790930f4 753
8ea22951
VK
754 return len;
755}
756
e76a4386
VK
757static DEVICE_ATTR_RO(total_trans);
758static DEVICE_ATTR_RO(time_in_state_ms);
759static DEVICE_ATTR_WO(reset);
760static DEVICE_ATTR_RO(trans_table);
8ea22951
VK
761
762static struct attribute *cooling_device_stats_attrs[] = {
763 &dev_attr_total_trans.attr,
764 &dev_attr_time_in_state_ms.attr,
765 &dev_attr_reset.attr,
766 &dev_attr_trans_table.attr,
767 NULL
768};
769
770static const struct attribute_group cooling_device_stats_attr_group = {
771 .attrs = cooling_device_stats_attrs,
772 .name = "stats"
773};
774
775static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
776{
d5a8aa5d 777 const struct attribute_group *stats_attr_group = NULL;
8ea22951 778 struct cooling_dev_stats *stats;
a365105c
VK
779 /* Total number of states is highest state + 1 */
780 unsigned long states = cdev->max_state + 1;
8ea22951
VK
781 int var;
782
8ea22951
VK
783 var = sizeof(*stats);
784 var += sizeof(*stats->time_in_state) * states;
785 var += sizeof(*stats->trans_table) * states * states;
786
787 stats = kzalloc(var, GFP_KERNEL);
788 if (!stats)
d5a8aa5d 789 goto out;
8ea22951
VK
790
791 stats->time_in_state = (ktime_t *)(stats + 1);
792 stats->trans_table = (unsigned int *)(stats->time_in_state + states);
793 cdev->stats = stats;
794 stats->last_time = ktime_get();
8ea22951
VK
795
796 spin_lock_init(&stats->lock);
797
d5a8aa5d
RW
798 stats_attr_group = &cooling_device_stats_attr_group;
799
800out:
8ea22951
VK
801 /* Fill the empty slot left in cooling_device_attr_groups */
802 var = ARRAY_SIZE(cooling_device_attr_groups) - 2;
d5a8aa5d 803 cooling_device_attr_groups[var] = stats_attr_group;
8ea22951
VK
804}
805
806static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
807{
808 kfree(cdev->stats);
809 cdev->stats = NULL;
810}
811
812#else
813
814static inline void
815cooling_device_stats_setup(struct thermal_cooling_device *cdev) {}
816static inline void
817cooling_device_stats_destroy(struct thermal_cooling_device *cdev) {}
818
819#endif /* CONFIG_THERMAL_STATISTICS */
820
45cf2ec9
EV
821void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev)
822{
8ea22951 823 cooling_device_stats_setup(cdev);
45cf2ec9
EV
824 cdev->device.groups = cooling_device_attr_groups;
825}
826
8ea22951
VK
827void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
828{
829 cooling_device_stats_destroy(cdev);
830}
831
790930f4
RW
832void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev)
833{
b57841fb
RW
834 lockdep_assert_held(&cdev->lock);
835
790930f4
RW
836 cooling_device_stats_destroy(cdev);
837 cooling_device_stats_setup(cdev);
838}
839
45cf2ec9
EV
840/* these helper will be used only at the time of bindig */
841ssize_t
33e678d4 842trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)
45cf2ec9 843{
0a0a40d7 844 struct thermal_zone_device *tz = to_thermal_zone(dev);
45cf2ec9
EV
845 struct thermal_instance *instance;
846
0a0a40d7 847 instance = container_of(attr, struct thermal_instance, attr);
45cf2ec9 848
0a0a40d7 849 return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip));
45cf2ec9
EV
850}
851
852ssize_t
33e678d4 853weight_show(struct device *dev, struct device_attribute *attr, char *buf)
45cf2ec9
EV
854{
855 struct thermal_instance *instance;
856
857 instance = container_of(attr, struct thermal_instance, weight_attr);
858
859 return sprintf(buf, "%d\n", instance->weight);
860}
861
33e678d4
VK
862ssize_t weight_store(struct device *dev, struct device_attribute *attr,
863 const char *buf, size_t count)
45cf2ec9 864{
0a0a40d7 865 struct thermal_zone_device *tz = to_thermal_zone(dev);
45cf2ec9
EV
866 struct thermal_instance *instance;
867 int ret, weight;
868
869 ret = kstrtoint(buf, 0, &weight);
870 if (ret)
871 return ret;
872
873 instance = container_of(attr, struct thermal_instance, weight_attr);
879c9dc5
LL
874
875 /* Don't race with governors using the 'weight' value */
cba00d16 876 guard(thermal_zone)(tz);
bfc57bd1 877
45cf2ec9 878 instance->weight = weight;
bfc57bd1 879
0a0a40d7 880 thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED);
bfc57bd1 881
45cf2ec9
EV
882 return count;
883}