272 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * OMAP thermal driver interface4 *5 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/6 * Contact:7 * Eduardo Valentin <eduardo.valentin@ti.com>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>17#include <linux/cpufreq.h>18#include <linux/cpumask.h>19#include <linux/cpu_cooling.h>20#include <linux/of.h>21 22#include "ti-thermal.h"23#include "ti-bandgap.h"24#include "../thermal_hwmon.h"25 26#define TI_BANDGAP_UPDATE_INTERVAL_MS 25027 28/* common data structures */29struct ti_thermal_data {30 struct cpufreq_policy *policy;31 struct thermal_zone_device *ti_thermal;32 struct thermal_zone_device *pcb_tz;33 struct thermal_cooling_device *cool_dev;34 struct ti_bandgap *bgp;35 enum thermal_device_mode mode;36 struct work_struct thermal_wq;37 int sensor_id;38 bool our_zone;39};40 41static void ti_thermal_work(struct work_struct *work)42{43 struct ti_thermal_data *data = container_of(work,44 struct ti_thermal_data, thermal_wq);45 46 thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);47 48 dev_dbg(data->bgp->dev, "updated thermal zone %s\n",49 thermal_zone_device_type(data->ti_thermal));50}51 52/**53 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature54 * @t: omap sensor temperature55 * @s: omap sensor slope value56 * @c: omap sensor const value57 */58static inline int ti_thermal_hotspot_temperature(int t, int s, int c)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 */69/* Get temperature callback function for thermal zone */70static inline int __ti_thermal_get_temp(struct thermal_zone_device *tz, int *temp)71{72 struct thermal_zone_device *pcb_tz = NULL;73 struct ti_thermal_data *data = thermal_zone_device_priv(tz);74 struct ti_bandgap *bgp;75 const struct ti_temp_sensor *s;76 int ret, tmp, slope, constant;77 int pcb_temp;78 79 if (!data)80 return 0;81 82 bgp = data->bgp;83 s = &bgp->conf->sensors[data->sensor_id];84 85 ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);86 if (ret)87 return ret;88 89 /* Default constants */90 slope = thermal_zone_get_slope(tz);91 constant = thermal_zone_get_offset(tz);92 93 pcb_tz = data->pcb_tz;94 /* In case pcb zone is available, use the extrapolation rule with it */95 if (!IS_ERR(pcb_tz)) {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");104 ret = 0;105 }106 }107 *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);108 109 return ret;110}111 112static int __ti_thermal_get_trend(struct thermal_zone_device *tz,113 const struct thermal_trip *trip,114 enum thermal_trend *trend)115{116 struct ti_thermal_data *data = thermal_zone_device_priv(tz);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 else132 *trend = THERMAL_TREND_STABLE;133 134 return 0;135}136 137static const struct thermal_zone_device_ops ti_of_thermal_ops = {138 .get_temp = __ti_thermal_get_temp,139 .get_trend = __ti_thermal_get_trend,140};141 142static struct ti_thermal_data143*ti_thermal_build_data(struct ti_bandgap *bgp, int id)144{145 struct ti_thermal_data *data;146 147 data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);148 if (!data) {149 dev_err(bgp->dev, "kzalloc fail\n");150 return NULL;151 }152 data->sensor_id = id;153 data->bgp = bgp;154 data->mode = THERMAL_DEVICE_ENABLED;155 /* pcb_tz will be either valid or PTR_ERR() */156 data->pcb_tz = thermal_zone_get_zone_by_name("pcb");157 INIT_WORK(&data->thermal_wq, ti_thermal_work);158 159 return data;160}161 162int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,163 char *domain)164{165 struct ti_thermal_data *data;166 167 data = ti_bandgap_get_sensor_data(bgp, id);168 169 if (IS_ERR_OR_NULL(data))170 data = ti_thermal_build_data(bgp, id);171 172 if (!data)173 return -EINVAL;174 175 /* in case this is specified by DT */176 data->ti_thermal = devm_thermal_of_zone_register(bgp->dev, id,177 data, &ti_of_thermal_ops);178 if (IS_ERR(data->ti_thermal)) {179 dev_err(bgp->dev, "thermal zone device is NULL\n");180 return PTR_ERR(data->ti_thermal);181 }182 183 ti_bandgap_set_sensor_data(bgp, id, data);184 ti_bandgap_write_update_interval(bgp, data->sensor_id,185 TI_BANDGAP_UPDATE_INTERVAL_MS);186 187 devm_thermal_add_hwmon_sysfs(bgp->dev, data->ti_thermal);188 189 return 0;190}191 192int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)193{194 struct ti_thermal_data *data;195 196 data = ti_bandgap_get_sensor_data(bgp, id);197 198 if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {199 if (data->our_zone)200 thermal_zone_device_unregister(data->ti_thermal);201 }202 203 return 0;204}205 206int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)207{208 struct ti_thermal_data *data;209 210 data = ti_bandgap_get_sensor_data(bgp, id);211 212 schedule_work(&data->thermal_wq);213 214 return 0;215}216 217int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)218{219 struct ti_thermal_data *data;220 struct device_node *np = bgp->dev->of_node;221 222 /*223 * We are assuming here that if one deploys the zone224 * using DT, then it must be aware that the cooling device225 * loading has to happen via cpufreq driver.226 */227 if (of_property_present(np, "#thermal-sensor-cells"))228 return 0;229 230 data = ti_bandgap_get_sensor_data(bgp, id);231 if (!data || IS_ERR(data))232 data = ti_thermal_build_data(bgp, id);233 234 if (!data)235 return -EINVAL;236 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 243 /* Register cooling device */244 data->cool_dev = cpufreq_cooling_register(data->policy);245 if (IS_ERR(data->cool_dev)) {246 int ret = PTR_ERR(data->cool_dev);247 dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",248 ret);249 cpufreq_cpu_put(data->policy);250 251 return ret;252 }253 ti_bandgap_set_sensor_data(bgp, id, data);254 255 return 0;256}257 258int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)259{260 struct ti_thermal_data *data;261 262 data = ti_bandgap_get_sensor_data(bgp, id);263 264 if (!IS_ERR_OR_NULL(data)) {265 cpufreq_cooling_unregister(data->cool_dev);266 if (data->policy)267 cpufreq_cpu_put(data->policy);268 }269 270 return 0;271}272