cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ti-thermal-common.c (6422B)


      1// SPDX-License-Identifier: GPL-2.0-only
      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>
      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/* common data structures */
     27struct ti_thermal_data {
     28	struct cpufreq_policy *policy;
     29	struct thermal_zone_device *ti_thermal;
     30	struct thermal_zone_device *pcb_tz;
     31	struct thermal_cooling_device *cool_dev;
     32	struct ti_bandgap *bgp;
     33	enum thermal_device_mode mode;
     34	struct work_struct thermal_wq;
     35	int sensor_id;
     36	bool our_zone;
     37};
     38
     39static void ti_thermal_work(struct work_struct *work)
     40{
     41	struct ti_thermal_data *data = container_of(work,
     42					struct ti_thermal_data, thermal_wq);
     43
     44	thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
     45
     46	dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
     47		data->ti_thermal->type);
     48}
     49
     50/**
     51 * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
     52 * @t:	omap sensor temperature
     53 * @s:	omap sensor slope value
     54 * @c:	omap sensor const value
     55 */
     56static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
     57{
     58	int delta = t * s / 1000 + c;
     59
     60	if (delta < 0)
     61		delta = 0;
     62
     63	return t + delta;
     64}
     65
     66/* thermal zone ops */
     67/* Get temperature callback function for thermal zone */
     68static inline int __ti_thermal_get_temp(void *devdata, int *temp)
     69{
     70	struct thermal_zone_device *pcb_tz = NULL;
     71	struct ti_thermal_data *data = devdata;
     72	struct ti_bandgap *bgp;
     73	const struct ti_temp_sensor *s;
     74	int ret, tmp, slope, constant;
     75	int pcb_temp;
     76
     77	if (!data)
     78		return 0;
     79
     80	bgp = data->bgp;
     81	s = &bgp->conf->sensors[data->sensor_id];
     82
     83	ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
     84	if (ret)
     85		return ret;
     86
     87	/* Default constants */
     88	slope = thermal_zone_get_slope(data->ti_thermal);
     89	constant = thermal_zone_get_offset(data->ti_thermal);
     90
     91	pcb_tz = data->pcb_tz;
     92	/* In case pcb zone is available, use the extrapolation rule with it */
     93	if (!IS_ERR(pcb_tz)) {
     94		ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
     95		if (!ret) {
     96			tmp -= pcb_temp; /* got a valid PCB temp */
     97			slope = s->slope_pcb;
     98			constant = s->constant_pcb;
     99		} else {
    100			dev_err(bgp->dev,
    101				"Failed to read PCB state. Using defaults\n");
    102			ret = 0;
    103		}
    104	}
    105	*temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
    106
    107	return ret;
    108}
    109
    110static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
    111{
    112	struct ti_thermal_data *data = p;
    113	struct ti_bandgap *bgp;
    114	int id, tr, ret = 0;
    115
    116	bgp = data->bgp;
    117	id = data->sensor_id;
    118
    119	ret = ti_bandgap_get_trend(bgp, id, &tr);
    120	if (ret)
    121		return ret;
    122
    123	if (tr > 0)
    124		*trend = THERMAL_TREND_RAISING;
    125	else if (tr < 0)
    126		*trend = THERMAL_TREND_DROPPING;
    127	else
    128		*trend = THERMAL_TREND_STABLE;
    129
    130	return 0;
    131}
    132
    133static const struct thermal_zone_of_device_ops ti_of_thermal_ops = {
    134	.get_temp = __ti_thermal_get_temp,
    135	.get_trend = __ti_thermal_get_trend,
    136};
    137
    138static struct ti_thermal_data
    139*ti_thermal_build_data(struct ti_bandgap *bgp, int id)
    140{
    141	struct ti_thermal_data *data;
    142
    143	data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
    144	if (!data) {
    145		dev_err(bgp->dev, "kzalloc fail\n");
    146		return NULL;
    147	}
    148	data->sensor_id = id;
    149	data->bgp = bgp;
    150	data->mode = THERMAL_DEVICE_ENABLED;
    151	/* pcb_tz will be either valid or PTR_ERR() */
    152	data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
    153	INIT_WORK(&data->thermal_wq, ti_thermal_work);
    154
    155	return data;
    156}
    157
    158int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
    159			     char *domain)
    160{
    161	struct ti_thermal_data *data;
    162	int interval;
    163
    164	data = ti_bandgap_get_sensor_data(bgp, id);
    165
    166	if (IS_ERR_OR_NULL(data))
    167		data = ti_thermal_build_data(bgp, id);
    168
    169	if (!data)
    170		return -EINVAL;
    171
    172	/* in case this is specified by DT */
    173	data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id,
    174					data, &ti_of_thermal_ops);
    175	if (IS_ERR(data->ti_thermal)) {
    176		dev_err(bgp->dev, "thermal zone device is NULL\n");
    177		return PTR_ERR(data->ti_thermal);
    178	}
    179
    180	interval = jiffies_to_msecs(data->ti_thermal->polling_delay_jiffies);
    181
    182	ti_bandgap_set_sensor_data(bgp, id, data);
    183	ti_bandgap_write_update_interval(bgp, data->sensor_id, interval);
    184
    185	if (devm_thermal_add_hwmon_sysfs(data->ti_thermal))
    186		dev_warn(bgp->dev, "failed to add hwmon sysfs attributes\n");
    187
    188	return 0;
    189}
    190
    191int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
    192{
    193	struct ti_thermal_data *data;
    194
    195	data = ti_bandgap_get_sensor_data(bgp, id);
    196
    197	if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
    198		if (data->our_zone)
    199			thermal_zone_device_unregister(data->ti_thermal);
    200	}
    201
    202	return 0;
    203}
    204
    205int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
    206{
    207	struct ti_thermal_data *data;
    208
    209	data = ti_bandgap_get_sensor_data(bgp, id);
    210
    211	schedule_work(&data->thermal_wq);
    212
    213	return 0;
    214}
    215
    216int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
    217{
    218	struct ti_thermal_data *data;
    219	struct device_node *np = bgp->dev->of_node;
    220
    221	/*
    222	 * We are assuming here that if one deploys the zone
    223	 * using DT, then it must be aware that the cooling device
    224	 * loading has to happen via cpufreq driver.
    225	 */
    226	if (of_find_property(np, "#thermal-sensor-cells", NULL))
    227		return 0;
    228
    229	data = ti_bandgap_get_sensor_data(bgp, id);
    230	if (!data || IS_ERR(data))
    231		data = ti_thermal_build_data(bgp, id);
    232
    233	if (!data)
    234		return -EINVAL;
    235
    236	data->policy = cpufreq_cpu_get(0);
    237	if (!data->policy) {
    238		pr_debug("%s: CPUFreq policy not found\n", __func__);
    239		return -EPROBE_DEFER;
    240	}
    241
    242	/* Register cooling device */
    243	data->cool_dev = cpufreq_cooling_register(data->policy);
    244	if (IS_ERR(data->cool_dev)) {
    245		int ret = PTR_ERR(data->cool_dev);
    246		dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",
    247			ret);
    248		cpufreq_cpu_put(data->policy);
    249
    250		return ret;
    251	}
    252	ti_bandgap_set_sensor_data(bgp, id, data);
    253
    254	return 0;
    255}
    256
    257int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
    258{
    259	struct ti_thermal_data *data;
    260
    261	data = ti_bandgap_get_sensor_data(bgp, id);
    262
    263	if (!IS_ERR_OR_NULL(data)) {
    264		cpufreq_cooling_unregister(data->cool_dev);
    265		if (data->policy)
    266			cpufreq_cpu_put(data->policy);
    267	}
    268
    269	return 0;
    270}