• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  Copyright (C) 2008 Intel Corp
4  *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
5  *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
6  *  Copyright 2022 Linaro Limited
7  *
8  * Thermal trips handling
9  */
10 #include "thermal_core.h"
11 
for_each_thermal_trip(struct thermal_zone_device * tz,int (* cb)(struct thermal_trip *,void *),void * data)12 int for_each_thermal_trip(struct thermal_zone_device *tz,
13 			  int (*cb)(struct thermal_trip *, void *),
14 			  void *data)
15 {
16 	int i, ret;
17 
18 	lockdep_assert_held(&tz->lock);
19 
20 	for (i = 0; i < tz->num_trips; i++) {
21 		ret = cb(&tz->trips[i], data);
22 		if (ret)
23 			return ret;
24 	}
25 
26 	return 0;
27 }
28 EXPORT_SYMBOL_GPL(for_each_thermal_trip);
29 
thermal_zone_get_num_trips(struct thermal_zone_device * tz)30 int thermal_zone_get_num_trips(struct thermal_zone_device *tz)
31 {
32 	return tz->num_trips;
33 }
34 EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
35 
36 /**
37  * __thermal_zone_set_trips - Computes the next trip points for the driver
38  * @tz: a pointer to a thermal zone device structure
39  *
40  * The function computes the next temperature boundaries by browsing
41  * the trip points. The result is the closer low and high trip points
42  * to the current temperature. These values are passed to the backend
43  * driver to let it set its own notification mechanism (usually an
44  * interrupt).
45  *
46  * This function must be called with tz->lock held. Both tz and tz->ops
47  * must be valid pointers.
48  *
49  * It does not return a value
50  */
__thermal_zone_set_trips(struct thermal_zone_device * tz)51 void __thermal_zone_set_trips(struct thermal_zone_device *tz)
52 {
53 	struct thermal_trip trip;
54 	int low = -INT_MAX, high = INT_MAX;
55 	int i, ret;
56 
57 	lockdep_assert_held(&tz->lock);
58 
59 	if (!tz->ops->set_trips)
60 		return;
61 
62 	for (i = 0; i < tz->num_trips; i++) {
63 		int trip_low;
64 
65 		ret = __thermal_zone_get_trip(tz, i , &trip);
66 		if (ret)
67 			return;
68 
69 		trip_low = trip.temperature - trip.hysteresis;
70 
71 		if (trip_low < tz->temperature && trip_low > low)
72 			low = trip_low;
73 
74 		if (trip.temperature > tz->temperature &&
75 		    trip.temperature < high)
76 			high = trip.temperature;
77 	}
78 
79 	/* No need to change trip points */
80 	if (tz->prev_low_trip == low && tz->prev_high_trip == high)
81 		return;
82 
83 	tz->prev_low_trip = low;
84 	tz->prev_high_trip = high;
85 
86 	dev_dbg(&tz->device,
87 		"new temperature boundaries: %d < x < %d\n", low, high);
88 
89 	/*
90 	 * Set a temperature window. When this window is left the driver
91 	 * must inform the thermal core via thermal_zone_device_update.
92 	 */
93 	ret = tz->ops->set_trips(tz, low, high);
94 	if (ret)
95 		dev_err(&tz->device, "Failed to set trips: %d\n", ret);
96 }
97 
__thermal_zone_get_trip(struct thermal_zone_device * tz,int trip_id,struct thermal_trip * trip)98 int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
99 			    struct thermal_trip *trip)
100 {
101 	if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip)
102 		return -EINVAL;
103 
104 	*trip = tz->trips[trip_id];
105 	return 0;
106 }
107 EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
108 
thermal_zone_get_trip(struct thermal_zone_device * tz,int trip_id,struct thermal_trip * trip)109 int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
110 			  struct thermal_trip *trip)
111 {
112 	int ret;
113 
114 	mutex_lock(&tz->lock);
115 	ret = __thermal_zone_get_trip(tz, trip_id, trip);
116 	mutex_unlock(&tz->lock);
117 
118 	return ret;
119 }
120 EXPORT_SYMBOL_GPL(thermal_zone_get_trip);
121 
thermal_zone_set_trip(struct thermal_zone_device * tz,int trip_id,const struct thermal_trip * trip)122 int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id,
123 			  const struct thermal_trip *trip)
124 {
125 	struct thermal_trip t;
126 	int ret;
127 
128 	if (!tz->ops->set_trip_temp && !tz->ops->set_trip_hyst && !tz->trips)
129 		return -EINVAL;
130 
131 	ret = __thermal_zone_get_trip(tz, trip_id, &t);
132 	if (ret)
133 		return ret;
134 
135 	if (t.type != trip->type)
136 		return -EINVAL;
137 
138 	if (t.temperature != trip->temperature && tz->ops->set_trip_temp) {
139 		ret = tz->ops->set_trip_temp(tz, trip_id, trip->temperature);
140 		if (ret)
141 			return ret;
142 	}
143 
144 	if (t.hysteresis != trip->hysteresis && tz->ops->set_trip_hyst) {
145 		ret = tz->ops->set_trip_hyst(tz, trip_id, trip->hysteresis);
146 		if (ret)
147 			return ret;
148 	}
149 
150 	if (tz->trips && (t.temperature != trip->temperature || t.hysteresis != trip->hysteresis))
151 		tz->trips[trip_id] = *trip;
152 
153 	thermal_notify_tz_trip_change(tz->id, trip_id, trip->type,
154 				      trip->temperature, trip->hysteresis);
155 
156 	__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
157 
158 	return 0;
159 }
160 
thermal_zone_trip_id(struct thermal_zone_device * tz,const struct thermal_trip * trip)161 int thermal_zone_trip_id(struct thermal_zone_device *tz,
162 			 const struct thermal_trip *trip)
163 {
164 	int i;
165 
166 	for (i = 0; i < tz->num_trips; i++) {
167 		if (&tz->trips[i] == trip)
168 			return i;
169 	}
170 
171 	return -ENODATA;
172 }
173