File Index
Files and directories
Sign in
FILE

thermal_sysfs.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thermal/thermal_sysfs.c C
  1// SPDX-License-Identifier: GPL-2.0
  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>
 11 */
 12
 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 14
 15#include <linux/container_of.h>
 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>
 21#include <linux/jiffies.h>
 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
 43	if (!ret)
 44		return sprintf(buf, "%d\n", temperature);
 45
 46	if (ret == -EAGAIN)
 47		return -ENODATA;
 48
 49	return ret;
 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);
 56
 57	guard(thermal_zone)(tz);
 58
 59	if (tz->mode == THERMAL_DEVICE_ENABLED)
 60		return sprintf(buf, "enabled\n");
 61
 62	return sprintf(buf, "disabled\n");
 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
 72	if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
 73		result = thermal_zone_device_enable(tz);
 74	else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
 75		result = thermal_zone_device_disable(tz);
 76	else
 77		result = -EINVAL;
 78
 79	if (result)
 80		return result;
 81
 82	return count;
 83}
 84
 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
 94static ssize_t
 95trip_point_type_show(struct device *dev, struct device_attribute *attr,
 96		     char *buf)
 97{
 98	struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
 99
100	return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type));
101}
102
103static ssize_t
104trip_point_temp_store(struct device *dev, struct device_attribute *attr,
105		      const char *buf, size_t count)
106{
107	struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
108	struct thermal_zone_device *tz = to_thermal_zone(dev);
109	int temp;
110
111	if (kstrtoint(buf, 10, &temp))
112		return -EINVAL;
113
114	guard(thermal_zone)(tz);
115
116	if (temp == trip->temperature)
117		return count;
118
119	/* Arrange the condition to avoid integer overflows. */
120	if (temp != THERMAL_TEMP_INVALID &&
121	    temp <= trip->hysteresis + THERMAL_TEMP_INVALID)
122		return -EINVAL;
123
124	if (tz->ops.set_trip_temp) {
125		int ret;
126
127		ret = tz->ops.set_trip_temp(tz, trip, temp);
128		if (ret)
129			return ret;
130	}
131
132	thermal_zone_set_trip_temp(tz, trip, temp);
133
134	__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
135
136	return count;
137}
138
139static ssize_t
140trip_point_temp_show(struct device *dev, struct device_attribute *attr,
141		     char *buf)
142{
143	struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
144
145	return sprintf(buf, "%d\n", READ_ONCE(trip->temperature));
146}
147
148static ssize_t
149trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
150		      const char *buf, size_t count)
151{
152	struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
153	struct thermal_zone_device *tz = to_thermal_zone(dev);
154	int hyst;
155
156	if (kstrtoint(buf, 10, &hyst) || hyst < 0)
157		return -EINVAL;
158
159	guard(thermal_zone)(tz);
160
161	if (hyst == trip->hysteresis)
162		return count;
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);
172		return count;
173	}
174
175	if (trip->temperature - hyst <= THERMAL_TEMP_INVALID)
176		return -EINVAL;
177
178	thermal_zone_set_trip_hyst(tz, trip, hyst);
179
180	__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
181
182	return count;
183}
184
185static ssize_t
186trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
187		     char *buf)
188{
189	struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
190
191	return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis));
192}
193
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
226#if (IS_ENABLED(CONFIG_THERMAL_EMULATION))
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);
232	int temperature;
233
234	if (kstrtoint(buf, 10, &temperature))
235		return -EINVAL;
236
237	guard(thermal_zone)(tz);
238
239	if (tz->ops.set_emul_temp) {
240		int ret;
241
242		ret = tz->ops.set_emul_temp(tz, temperature);
243		if (ret)
244			return ret;
245	} else {
246		tz->emul_temperature = temperature;
247	}
248
249	__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
250
251	return count;
252}
253static DEVICE_ATTR_WO(emul_temp);
254#endif
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	}								\
316	static DEVICE_ATTR_RW(name)
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 */
332static DEVICE_ATTR_RO(type);
333static DEVICE_ATTR_RO(temp);
334static DEVICE_ATTR_RW(policy);
335static DEVICE_ATTR_RO(available_policies);
336static DEVICE_ATTR_RW(sustainable_power);
337
338/* These thermal zone device attributes are created based on conditions */
339static DEVICE_ATTR_RW(mode);
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
361static const struct attribute_group thermal_zone_attribute_group = {
362	.attrs = thermal_zone_dev_attrs,
363};
364
365static struct attribute *thermal_zone_mode_attrs[] = {
366	&dev_attr_mode.attr,
367	NULL,
368};
369
370static const struct attribute_group thermal_zone_mode_attribute_group = {
371	.attrs = thermal_zone_mode_attrs,
372};
373
374static const struct attribute_group *thermal_zone_attribute_groups[] = {
375	&thermal_zone_attribute_group,
376	&thermal_zone_mode_attribute_group,
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
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 */
389static int create_trip_attrs(struct thermal_zone_device *tz)
390{
391	struct thermal_trip_desc *td;
392	struct attribute **attrs;
393	int i;
394
395	attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL);
396	if (!attrs)
397		return -ENOMEM;
398
399	i = 0;
400	for_each_trip_desc(tz, td) {
401		struct thermal_trip_attrs *trip_attrs = &td->trip_attrs;
402
403		/* create trip type attribute */
404		snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH,
405			 "trip_point_%d_type", i);
406
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;
412
413		/* create trip temp attribute */
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;
421		if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) {
422			trip_attrs->temp.attr.attr.mode |= S_IWUSR;
423			trip_attrs->temp.attr.store = trip_point_temp_store;
424		}
425		attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr;
426
427		snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH,
428			 "trip_point_%d_hyst", i);
429
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;
434		if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) {
435			trip_attrs->hyst.attr.attr.mode |= S_IWUSR;
436			trip_attrs->hyst.attr.store = trip_point_hyst_store;
437		}
438		attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr;
439		i++;
440	}
441	attrs[tz->num_trips * 3] = NULL;
442
443	tz->trips_attribute_group.attrs = attrs;
444
445	return 0;
446}
447
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{
456	if (tz)
457		kfree(tz->trips_attribute_group.attrs);
458}
459
460int thermal_zone_create_device_groups(struct thermal_zone_device *tz)
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
475	if (tz->num_trips) {
476		result = create_trip_attrs(tz);
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}
490
491void thermal_zone_destroy_device_groups(struct thermal_zone_device *tz)
492{
493	if (!tz)
494		return;
495
496	if (tz->num_trips)
497		destroy_trip_attrs(tz);
498
499	kfree(tz->device.groups);
500}
501
502/* sys I/F for cooling device */
503static ssize_t
504cdev_type_show(struct device *dev, struct device_attribute *attr, char *buf)
505{
506	struct thermal_cooling_device *cdev = to_cooling_device(dev);
507
508	return sprintf(buf, "%s\n", cdev->type);
509}
510
511static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
512			      char *buf)
513{
514	struct thermal_cooling_device *cdev = to_cooling_device(dev);
515
516	return sprintf(buf, "%ld\n", cdev->max_state);
517}
518
519static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
520			      char *buf)
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
533cur_state_store(struct device *dev, struct device_attribute *attr,
534		const char *buf, size_t count)
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
546	/* Requested state should be less than max_state + 1 */
547	if (state > cdev->max_state)
548		return -EINVAL;
549
550	guard(cooling_dev)(cdev);
551
552	result = cdev->ops->set_cur_state(cdev, state);
553	if (result)
554		return result;
555
556	thermal_cooling_device_stats_update(cdev, state);
557
558	return count;
559}
560
561static struct device_attribute
562dev_attr_cdev_type = __ATTR(type, 0444, cdev_type_show, NULL);
563static DEVICE_ATTR_RO(max_state);
564static DEVICE_ATTR_RW(cur_state);
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,
579	NULL, /* Space allocated for cooling_device_stats_attr_group */
580	NULL,
581};
582
583#ifdef CONFIG_THERMAL_STATISTICS
584struct cooling_dev_stats {
585	spinlock_t lock;
586	unsigned int total_trans;
587	unsigned long state;
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
608	lockdep_assert_held(&cdev->lock);
609
610	if (!stats)
611		return;
612
613	spin_lock(&stats->lock);
614
615	if (stats->state == new_state)
616		goto unlock;
617
618	update_time_in_state(stats);
619	stats->trans_table[stats->state * (cdev->max_state + 1) + new_state]++;
620	stats->state = new_state;
621	stats->total_trans++;
622
623unlock:
624	spin_unlock(&stats->lock);
625}
626
627static ssize_t total_trans_show(struct device *dev,
628				struct device_attribute *attr, char *buf)
629{
630	struct thermal_cooling_device *cdev = to_cooling_device(dev);
631	struct cooling_dev_stats *stats;
632	int ret;
633
634	guard(cooling_dev)(cdev);
635
636	stats = cdev->stats;
637	if (!stats)
638		return 0;
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
648time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
649		      char *buf)
650{
651	struct thermal_cooling_device *cdev = to_cooling_device(dev);
652	struct cooling_dev_stats *stats;
653	ssize_t len = 0;
654	int i;
655
656	guard(cooling_dev)(cdev);
657
658	stats = cdev->stats;
659	if (!stats)
660		return 0;
661
662	spin_lock(&stats->lock);
663
664	update_time_in_state(stats);
665
666	for (i = 0; i <= cdev->max_state; i++) {
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
676reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
677	    size_t count)
678{
679	struct thermal_cooling_device *cdev = to_cooling_device(dev);
680	struct cooling_dev_stats *stats;
681	int i, states;
682
683	guard(cooling_dev)(cdev);
684
685	stats = cdev->stats;
686	if (!stats)
687		return count;
688
689	states = cdev->max_state + 1;
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
698	for (i = 0; i < states; i++)
699		stats->time_in_state[i] = ktime_set(0, 0);
700
701	spin_unlock(&stats->lock);
702
703	return count;
704}
705
706static ssize_t trans_table_show(struct device *dev,
707				struct device_attribute *attr, char *buf)
708{
709	struct thermal_cooling_device *cdev = to_cooling_device(dev);
710	struct cooling_dev_stats *stats;
711	ssize_t len = 0;
712	int i, j;
713
714	guard(cooling_dev)(cdev);
715
716	stats = cdev->stats;
717	if (!stats)
718		return -ENODATA;
719
720	len += snprintf(buf + len, PAGE_SIZE - len, " From  :    To\n");
721	len += snprintf(buf + len, PAGE_SIZE - len, "       : ");
722	for (i = 0; i <= cdev->max_state; i++) {
723		if (len >= PAGE_SIZE)
724			break;
725		len += snprintf(buf + len, PAGE_SIZE - len, "state%2u  ", i);
726	}
727	if (len >= PAGE_SIZE)
728		return PAGE_SIZE;
729
730	len += snprintf(buf + len, PAGE_SIZE - len, "\n");
731
732	for (i = 0; i <= cdev->max_state; i++) {
733		if (len >= PAGE_SIZE)
734			break;
735
736		len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
737
738		for (j = 0; j <= cdev->max_state; j++) {
739			if (len >= PAGE_SIZE)
740				break;
741			len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
742				stats->trans_table[i * (cdev->max_state + 1) + j]);
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");
751		len = -EFBIG;
752	}
753
754	return len;
755}
756
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);
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{
777	const struct attribute_group *stats_attr_group = NULL;
778	struct cooling_dev_stats *stats;
779	/* Total number of states is highest state + 1 */
780	unsigned long states = cdev->max_state + 1;
781	int var;
782
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)
789		goto out;
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();
795
796	spin_lock_init(&stats->lock);
797
798	stats_attr_group = &cooling_device_stats_attr_group;
799
800out:
801	/* Fill the empty slot left in cooling_device_attr_groups */
802	var = ARRAY_SIZE(cooling_device_attr_groups) - 2;
803	cooling_device_attr_groups[var] = stats_attr_group;
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
821void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev)
822{
823	cooling_device_stats_setup(cdev);
824	cdev->device.groups = cooling_device_attr_groups;
825}
826
827void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
828{
829	cooling_device_stats_destroy(cdev);
830}
831
832void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev)
833{
834	lockdep_assert_held(&cdev->lock);
835
836	cooling_device_stats_destroy(cdev);
837	cooling_device_stats_setup(cdev);
838}
839
840/* these helper will be used only at the time of bindig */
841ssize_t
842trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)
843{
844	struct thermal_zone_device *tz = to_thermal_zone(dev);
845	struct thermal_instance *instance;
846
847	instance = container_of(attr, struct thermal_instance, attr);
848
849	return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip));
850}
851
852ssize_t
853weight_show(struct device *dev, struct device_attribute *attr, char *buf)
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
862ssize_t weight_store(struct device *dev, struct device_attribute *attr,
863		     const char *buf, size_t count)
864{
865	struct thermal_zone_device *tz = to_thermal_zone(dev);
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);
874
875	/* Don't race with governors using the 'weight' value */
876	guard(thermal_zone)(tz);
877
878	instance->weight = weight;
879
880	thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED);
881
882	return count;
883}
~/ Read-only file preview.