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 | |
12 | int for_each_thermal_trip(struct thermal_zone_device *tz, |
13 | int (*cb)(struct thermal_trip *, void *), |
14 | void *data) |
15 | { |
16 | struct thermal_trip *trip; |
17 | int ret; |
18 | |
19 | for_each_trip(tz, trip) { |
20 | ret = cb(trip, data); |
21 | if (ret) |
22 | return ret; |
23 | } |
24 | |
25 | return 0; |
26 | } |
27 | EXPORT_SYMBOL_GPL(for_each_thermal_trip); |
28 | |
29 | int thermal_zone_for_each_trip(struct thermal_zone_device *tz, |
30 | int (*cb)(struct thermal_trip *, void *), |
31 | void *data) |
32 | { |
33 | int ret; |
34 | |
35 | mutex_lock(&tz->lock); |
36 | ret = for_each_thermal_trip(tz, cb, data); |
37 | mutex_unlock(lock: &tz->lock); |
38 | |
39 | return ret; |
40 | } |
41 | EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); |
42 | |
43 | int thermal_zone_get_num_trips(struct thermal_zone_device *tz) |
44 | { |
45 | return tz->num_trips; |
46 | } |
47 | EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); |
48 | |
49 | /** |
50 | * __thermal_zone_set_trips - Computes the next trip points for the driver |
51 | * @tz: a pointer to a thermal zone device structure |
52 | * |
53 | * The function computes the next temperature boundaries by browsing |
54 | * the trip points. The result is the closer low and high trip points |
55 | * to the current temperature. These values are passed to the backend |
56 | * driver to let it set its own notification mechanism (usually an |
57 | * interrupt). |
58 | * |
59 | * This function must be called with tz->lock held. Both tz and tz->ops |
60 | * must be valid pointers. |
61 | * |
62 | * It does not return a value |
63 | */ |
64 | void __thermal_zone_set_trips(struct thermal_zone_device *tz) |
65 | { |
66 | const struct thermal_trip *trip; |
67 | int low = -INT_MAX, high = INT_MAX; |
68 | int ret; |
69 | |
70 | lockdep_assert_held(&tz->lock); |
71 | |
72 | if (!tz->ops.set_trips) |
73 | return; |
74 | |
75 | for_each_trip(tz, trip) { |
76 | int trip_low; |
77 | |
78 | trip_low = trip->temperature - trip->hysteresis; |
79 | |
80 | if (trip_low < tz->temperature && trip_low > low) |
81 | low = trip_low; |
82 | |
83 | if (trip->temperature > tz->temperature && |
84 | trip->temperature < high) |
85 | high = trip->temperature; |
86 | } |
87 | |
88 | /* No need to change trip points */ |
89 | if (tz->prev_low_trip == low && tz->prev_high_trip == high) |
90 | return; |
91 | |
92 | tz->prev_low_trip = low; |
93 | tz->prev_high_trip = high; |
94 | |
95 | dev_dbg(&tz->device, |
96 | "new temperature boundaries: %d < x < %d\n" , low, high); |
97 | |
98 | /* |
99 | * Set a temperature window. When this window is left the driver |
100 | * must inform the thermal core via thermal_zone_device_update. |
101 | */ |
102 | ret = tz->ops.set_trips(tz, low, high); |
103 | if (ret) |
104 | dev_err(&tz->device, "Failed to set trips: %d\n" , ret); |
105 | } |
106 | |
107 | int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, |
108 | struct thermal_trip *trip) |
109 | { |
110 | if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip) |
111 | return -EINVAL; |
112 | |
113 | *trip = tz->trips[trip_id]; |
114 | return 0; |
115 | } |
116 | EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); |
117 | |
118 | int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, |
119 | struct thermal_trip *trip) |
120 | { |
121 | int ret; |
122 | |
123 | mutex_lock(&tz->lock); |
124 | ret = __thermal_zone_get_trip(tz, trip_id, trip); |
125 | mutex_unlock(lock: &tz->lock); |
126 | |
127 | return ret; |
128 | } |
129 | EXPORT_SYMBOL_GPL(thermal_zone_get_trip); |
130 | |
131 | int thermal_zone_trip_id(const struct thermal_zone_device *tz, |
132 | const struct thermal_trip *trip) |
133 | { |
134 | /* |
135 | * Assume the trip to be located within the bounds of the thermal |
136 | * zone's trips[] table. |
137 | */ |
138 | return trip - tz->trips; |
139 | } |
140 | void thermal_zone_trip_updated(struct thermal_zone_device *tz, |
141 | const struct thermal_trip *trip) |
142 | { |
143 | thermal_notify_tz_trip_change(tz, trip); |
144 | __thermal_zone_device_update(tz, event: THERMAL_TRIP_CHANGED); |
145 | } |
146 | |
147 | void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, |
148 | struct thermal_trip *trip, int temp) |
149 | { |
150 | if (trip->temperature == temp) |
151 | return; |
152 | |
153 | trip->temperature = temp; |
154 | thermal_notify_tz_trip_change(tz, trip); |
155 | } |
156 | EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); |
157 | |