Merge tag 'vfs-6.7.misc' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs
[linux-block.git] / drivers / thermal / ti-soc-thermal / ti-thermal-common.c
CommitLineData
2b27bdcc 1// SPDX-License-Identifier: GPL-2.0-only
445eaf87
EV
2/*
3 * OMAP thermal driver interface
4 *
5 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
6 * Contact:
7 * Eduardo Valentin <eduardo.valentin@ti.com>
445eaf87
EV
8 */
9
10#include <linux/device.h>
11#include <linux/err.h>
12#include <linux/mutex.h>
13#include <linux/gfp.h>
14#include <linux/kernel.h>
15#include <linux/workqueue.h>
16#include <linux/thermal.h>
4d753aa7 17#include <linux/cpufreq.h>
5035d48d 18#include <linux/cpumask.h>
445eaf87 19#include <linux/cpu_cooling.h>
26d9cc65 20#include <linux/of.h>
445eaf87 21
7372add4
EV
22#include "ti-thermal.h"
23#include "ti-bandgap.h"
3a9abd6c 24#include "../thermal_hwmon.h"
445eaf87 25
0c492be4
DL
26#define TI_BANDGAP_UPDATE_INTERVAL_MS 250
27
445eaf87 28/* common data structures */
03e859d3 29struct ti_thermal_data {
4d753aa7 30 struct cpufreq_policy *policy;
03e859d3 31 struct thermal_zone_device *ti_thermal;
359836e1 32 struct thermal_zone_device *pcb_tz;
445eaf87 33 struct thermal_cooling_device *cool_dev;
03e859d3 34 struct ti_bandgap *bgp;
445eaf87
EV
35 enum thermal_device_mode mode;
36 struct work_struct thermal_wq;
37 int sensor_id;
26d9cc65 38 bool our_zone;
445eaf87
EV
39};
40
03e859d3 41static void ti_thermal_work(struct work_struct *work)
445eaf87 42{
03e859d3
EV
43 struct ti_thermal_data *data = container_of(work,
44 struct ti_thermal_data, thermal_wq);
445eaf87 45
0e70f466 46 thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
445eaf87 47
dec07d39 48 dev_dbg(data->bgp->dev, "updated thermal zone %s\n",
dbb0ea15 49 thermal_zone_device_type(data->ti_thermal));
445eaf87
EV
50}
51
52/**
03e859d3 53 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
445eaf87
EV
54 * @t: omap sensor temperature
55 * @s: omap sensor slope value
56 * @c: omap sensor const value
57 */
03e859d3 58static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
445eaf87
EV
59{
60 int delta = t * s / 1000 + c;
61
62 if (delta < 0)
63 delta = 0;
64
65 return t + delta;
66}
67
68/* thermal zone ops */
e34238bf 69/* Get temperature callback function for thermal zone */
2cf3c72a 70static inline int __ti_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
445eaf87 71{
359836e1 72 struct thermal_zone_device *pcb_tz = NULL;
5f68d078 73 struct ti_thermal_data *data = thermal_zone_device_priv(tz);
03e859d3 74 struct ti_bandgap *bgp;
9879b2c4 75 const struct ti_temp_sensor *s;
359836e1 76 int ret, tmp, slope, constant;
17e8351a 77 int pcb_temp;
445eaf87 78
04a4d10d
EV
79 if (!data)
80 return 0;
81
d7f080e6
EV
82 bgp = data->bgp;
83 s = &bgp->conf->sensors[data->sensor_id];
04a4d10d 84
03e859d3 85 ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
445eaf87
EV
86 if (ret)
87 return ret;
88
359836e1 89 /* Default constants */
2cf3c72a
DL
90 slope = thermal_zone_get_slope(tz);
91 constant = thermal_zone_get_offset(tz);
359836e1
EV
92
93 pcb_tz = data->pcb_tz;
445eaf87 94 /* In case pcb zone is available, use the extrapolation rule with it */
0c12b5ac 95 if (!IS_ERR(pcb_tz)) {
359836e1
EV
96 ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
97 if (!ret) {
98 tmp -= pcb_temp; /* got a valid PCB temp */
99 slope = s->slope_pcb;
100 constant = s->constant_pcb;
101 } else {
102 dev_err(bgp->dev,
103 "Failed to read PCB state. Using defaults\n");
df8f1347 104 ret = 0;
359836e1 105 }
445eaf87 106 }
03e859d3 107 *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
445eaf87
EV
108
109 return ret;
110}
111
8289d810 112static int __ti_thermal_get_trend(struct thermal_zone_device *tz,
ebc7abb3
RW
113 const struct thermal_trip *trip,
114 enum thermal_trend *trend)
03b7f67b 115{
5f68d078 116 struct ti_thermal_data *data = thermal_zone_device_priv(tz);
03b7f67b
K
117 struct ti_bandgap *bgp;
118 int id, tr, ret = 0;
119
120 bgp = data->bgp;
121 id = data->sensor_id;
122
123 ret = ti_bandgap_get_trend(bgp, id, &tr);
124 if (ret)
125 return ret;
126
127 if (tr > 0)
128 *trend = THERMAL_TREND_RAISING;
129 else if (tr < 0)
130 *trend = THERMAL_TREND_DROPPING;
131 else
132 *trend = THERMAL_TREND_STABLE;
133
134 return 0;
135}
136
2cf3c72a 137static const struct thermal_zone_device_ops ti_of_thermal_ops = {
2251aef6
EV
138 .get_temp = __ti_thermal_get_temp,
139 .get_trend = __ti_thermal_get_trend,
140};
141
03e859d3
EV
142static struct ti_thermal_data
143*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
445eaf87 144{
03e859d3 145 struct ti_thermal_data *data;
445eaf87 146
d7f080e6 147 data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
445eaf87 148 if (!data) {
d7f080e6 149 dev_err(bgp->dev, "kzalloc fail\n");
04a4d10d 150 return NULL;
445eaf87
EV
151 }
152 data->sensor_id = id;
d7f080e6 153 data->bgp = bgp;
445eaf87 154 data->mode = THERMAL_DEVICE_ENABLED;
0c12b5ac 155 /* pcb_tz will be either valid or PTR_ERR() */
359836e1 156 data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
03e859d3 157 INIT_WORK(&data->thermal_wq, ti_thermal_work);
445eaf87 158
04a4d10d
EV
159 return data;
160}
161
03e859d3
EV
162int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
163 char *domain)
04a4d10d 164{
03e859d3 165 struct ti_thermal_data *data;
04a4d10d 166
03e859d3 167 data = ti_bandgap_get_sensor_data(bgp, id);
04a4d10d 168
0f348db0 169 if (IS_ERR_OR_NULL(data))
03e859d3 170 data = ti_thermal_build_data(bgp, id);
04a4d10d
EV
171
172 if (!data)
173 return -EINVAL;
174
26d9cc65 175 /* in case this is specified by DT */
2cf3c72a 176 data->ti_thermal = devm_thermal_of_zone_register(bgp->dev, id,
2251aef6 177 data, &ti_of_thermal_ops);
26d9cc65 178 if (IS_ERR(data->ti_thermal)) {
b263b473
K
179 dev_err(bgp->dev, "thermal zone device is NULL\n");
180 return PTR_ERR(data->ti_thermal);
445eaf87 181 }
b263b473 182
03e859d3 183 ti_bandgap_set_sensor_data(bgp, id, data);
0c492be4
DL
184 ti_bandgap_write_update_interval(bgp, data->sensor_id,
185 TI_BANDGAP_UPDATE_INTERVAL_MS);
445eaf87 186
a4ebd423 187 devm_thermal_add_hwmon_sysfs(bgp->dev, data->ti_thermal);
3a9abd6c 188
445eaf87
EV
189 return 0;
190}
191
03e859d3 192int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
445eaf87 193{
03e859d3 194 struct ti_thermal_data *data;
445eaf87 195
03e859d3 196 data = ti_bandgap_get_sensor_data(bgp, id);
445eaf87 197
7440f518 198 if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
26d9cc65
EV
199 if (data->our_zone)
200 thermal_zone_device_unregister(data->ti_thermal);
26d9cc65 201 }
445eaf87
EV
202
203 return 0;
204}
205
03e859d3 206int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
445eaf87 207{
03e859d3 208 struct ti_thermal_data *data;
445eaf87 209
03e859d3 210 data = ti_bandgap_get_sensor_data(bgp, id);
445eaf87
EV
211
212 schedule_work(&data->thermal_wq);
213
214 return 0;
215}
216
03e859d3 217int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
445eaf87 218{
03e859d3 219 struct ti_thermal_data *data;
26d9cc65
EV
220 struct device_node *np = bgp->dev->of_node;
221
222 /*
223 * We are assuming here that if one deploys the zone
224 * using DT, then it must be aware that the cooling device
225 * loading has to happen via cpufreq driver.
226 */
86df7d19 227 if (of_property_present(np, "#thermal-sensor-cells"))
26d9cc65 228 return 0;
445eaf87 229
03e859d3 230 data = ti_bandgap_get_sensor_data(bgp, id);
0c12b5ac 231 if (!data || IS_ERR(data))
03e859d3 232 data = ti_thermal_build_data(bgp, id);
04a4d10d
EV
233
234 if (!data)
235 return -EINVAL;
445eaf87 236
4d753aa7
VK
237 data->policy = cpufreq_cpu_get(0);
238 if (!data->policy) {
239 pr_debug("%s: CPUFreq policy not found\n", __func__);
240 return -EPROBE_DEFER;
241 }
242
445eaf87 243 /* Register cooling device */
4d753aa7 244 data->cool_dev = cpufreq_cooling_register(data->policy);
0c12b5ac 245 if (IS_ERR(data->cool_dev)) {
cffafc32 246 int ret = PTR_ERR(data->cool_dev);
4d753aa7
VK
247 dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",
248 ret);
249 cpufreq_cpu_put(data->policy);
cffafc32
EV
250
251 return ret;
445eaf87 252 }
03e859d3 253 ti_bandgap_set_sensor_data(bgp, id, data);
445eaf87
EV
254
255 return 0;
256}
257
03e859d3 258int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
445eaf87 259{
03e859d3 260 struct ti_thermal_data *data;
445eaf87 261
03e859d3 262 data = ti_bandgap_get_sensor_data(bgp, id);
26d9cc65 263
7440f518 264 if (!IS_ERR_OR_NULL(data)) {
26d9cc65 265 cpufreq_cooling_unregister(data->cool_dev);
ba817a8c
TL
266 if (data->policy)
267 cpufreq_cpu_put(data->policy);
4d753aa7 268 }
445eaf87
EV
269
270 return 0;
271}