brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 0cf0826 Raw
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