1 /* 2 * OMAP thermal driver interface 3 * 4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ 5 * Contact: 6 * Eduardo Valentin <eduardo.valentin@ti.com> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * version 2 as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA 21 * 22 */ 23 24 #include <linux/device.h> 25 #include <linux/err.h> 26 #include <linux/mutex.h> 27 #include <linux/gfp.h> 28 #include <linux/kernel.h> 29 #include <linux/workqueue.h> 30 #include <linux/thermal.h> 31 #include <linux/cpufreq.h> 32 #include <linux/cpumask.h> 33 #include <linux/cpu_cooling.h> 34 #include <linux/of.h> 35 36 #include "ti-thermal.h" 37 #include "ti-bandgap.h" 38 39 /* common data structures */ 40 struct ti_thermal_data { 41 struct cpufreq_policy *policy; 42 struct thermal_zone_device *ti_thermal; 43 struct thermal_zone_device *pcb_tz; 44 struct thermal_cooling_device *cool_dev; 45 struct ti_bandgap *bgp; 46 enum thermal_device_mode mode; 47 struct work_struct thermal_wq; 48 int sensor_id; 49 bool our_zone; 50 }; 51 52 static void ti_thermal_work(struct work_struct *work) 53 { 54 struct ti_thermal_data *data = container_of(work, 55 struct ti_thermal_data, thermal_wq); 56 57 thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED); 58 59 dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n", 60 data->ti_thermal->type); 61 } 62 63 /** 64 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature 65 * @t: omap sensor temperature 66 * @s: omap sensor slope value 67 * @c: omap sensor const value 68 */ 69 static inline int ti_thermal_hotspot_temperature(int t, int s, int c) 70 { 71 int delta = t * s / 1000 + c; 72 73 if (delta < 0) 74 delta = 0; 75 76 return t + delta; 77 } 78 79 /* thermal zone ops */ 80 /* Get temperature callback function for thermal zone */ 81 static inline int __ti_thermal_get_temp(void *devdata, int *temp) 82 { 83 struct thermal_zone_device *pcb_tz = NULL; 84 struct ti_thermal_data *data = devdata; 85 struct ti_bandgap *bgp; 86 const struct ti_temp_sensor *s; 87 int ret, tmp, slope, constant; 88 int pcb_temp; 89 90 if (!data) 91 return 0; 92 93 bgp = data->bgp; 94 s = &bgp->conf->sensors[data->sensor_id]; 95 96 ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp); 97 if (ret) 98 return ret; 99 100 /* Default constants */ 101 slope = thermal_zone_get_slope(data->ti_thermal); 102 constant = thermal_zone_get_offset(data->ti_thermal); 103 104 pcb_tz = data->pcb_tz; 105 /* In case pcb zone is available, use the extrapolation rule with it */ 106 if (!IS_ERR(pcb_tz)) { 107 ret = thermal_zone_get_temp(pcb_tz, &pcb_temp); 108 if (!ret) { 109 tmp -= pcb_temp; /* got a valid PCB temp */ 110 slope = s->slope_pcb; 111 constant = s->constant_pcb; 112 } else { 113 dev_err(bgp->dev, 114 "Failed to read PCB state. Using defaults\n"); 115 ret = 0; 116 } 117 } 118 *temp = ti_thermal_hotspot_temperature(tmp, slope, constant); 119 120 return ret; 121 } 122 123 static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal, 124 int *temp) 125 { 126 struct ti_thermal_data *data = thermal->devdata; 127 128 return __ti_thermal_get_temp(data, temp); 129 } 130 131 static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend) 132 { 133 struct ti_thermal_data *data = p; 134 struct ti_bandgap *bgp; 135 int id, tr, ret = 0; 136 137 bgp = data->bgp; 138 id = data->sensor_id; 139 140 ret = ti_bandgap_get_trend(bgp, id, &tr); 141 if (ret) 142 return ret; 143 144 if (tr > 0) 145 *trend = THERMAL_TREND_RAISING; 146 else if (tr < 0) 147 *trend = THERMAL_TREND_DROPPING; 148 else 149 *trend = THERMAL_TREND_STABLE; 150 151 return 0; 152 } 153 154 static const struct thermal_zone_of_device_ops ti_of_thermal_ops = { 155 .get_temp = __ti_thermal_get_temp, 156 .get_trend = __ti_thermal_get_trend, 157 }; 158 159 static struct ti_thermal_data 160 *ti_thermal_build_data(struct ti_bandgap *bgp, int id) 161 { 162 struct ti_thermal_data *data; 163 164 data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL); 165 if (!data) { 166 dev_err(bgp->dev, "kzalloc fail\n"); 167 return NULL; 168 } 169 data->sensor_id = id; 170 data->bgp = bgp; 171 data->mode = THERMAL_DEVICE_ENABLED; 172 /* pcb_tz will be either valid or PTR_ERR() */ 173 data->pcb_tz = thermal_zone_get_zone_by_name("pcb"); 174 INIT_WORK(&data->thermal_wq, ti_thermal_work); 175 176 return data; 177 } 178 179 int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, 180 char *domain) 181 { 182 struct ti_thermal_data *data; 183 184 data = ti_bandgap_get_sensor_data(bgp, id); 185 186 if (!data || IS_ERR(data)) 187 data = ti_thermal_build_data(bgp, id); 188 189 if (!data) 190 return -EINVAL; 191 192 /* in case this is specified by DT */ 193 data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id, 194 data, &ti_of_thermal_ops); 195 if (IS_ERR(data->ti_thermal)) { 196 dev_err(bgp->dev, "thermal zone device is NULL\n"); 197 return PTR_ERR(data->ti_thermal); 198 } 199 200 ti_bandgap_set_sensor_data(bgp, id, data); 201 ti_bandgap_write_update_interval(bgp, data->sensor_id, 202 data->ti_thermal->polling_delay); 203 204 return 0; 205 } 206 207 int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id) 208 { 209 struct ti_thermal_data *data; 210 211 data = ti_bandgap_get_sensor_data(bgp, id); 212 213 if (data && data->ti_thermal) { 214 if (data->our_zone) 215 thermal_zone_device_unregister(data->ti_thermal); 216 } 217 218 return 0; 219 } 220 221 int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id) 222 { 223 struct ti_thermal_data *data; 224 225 data = ti_bandgap_get_sensor_data(bgp, id); 226 227 schedule_work(&data->thermal_wq); 228 229 return 0; 230 } 231 232 int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) 233 { 234 struct ti_thermal_data *data; 235 struct device_node *np = bgp->dev->of_node; 236 237 /* 238 * We are assuming here that if one deploys the zone 239 * using DT, then it must be aware that the cooling device 240 * loading has to happen via cpufreq driver. 241 */ 242 if (of_find_property(np, "#thermal-sensor-cells", NULL)) 243 return 0; 244 245 data = ti_bandgap_get_sensor_data(bgp, id); 246 if (!data || IS_ERR(data)) 247 data = ti_thermal_build_data(bgp, id); 248 249 if (!data) 250 return -EINVAL; 251 252 data->policy = cpufreq_cpu_get(0); 253 if (!data->policy) { 254 pr_debug("%s: CPUFreq policy not found\n", __func__); 255 return -EPROBE_DEFER; 256 } 257 258 /* Register cooling device */ 259 data->cool_dev = cpufreq_cooling_register(data->policy); 260 if (IS_ERR(data->cool_dev)) { 261 int ret = PTR_ERR(data->cool_dev); 262 dev_err(bgp->dev, "Failed to register cpu cooling device %d\n", 263 ret); 264 cpufreq_cpu_put(data->policy); 265 266 return ret; 267 } 268 ti_bandgap_set_sensor_data(bgp, id, data); 269 270 return 0; 271 } 272 273 int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) 274 { 275 struct ti_thermal_data *data; 276 277 data = ti_bandgap_get_sensor_data(bgp, id); 278 279 if (data) { 280 cpufreq_cooling_unregister(data->cool_dev); 281 if (data->policy) 282 cpufreq_cpu_put(data->policy); 283 } 284 285 return 0; 286 } 287