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

st_thermal.c (6924B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * ST Thermal Sensor Driver core routines
      4 * Author: Ajit Pal Singh <ajitpal.singh@st.com>
      5 *
      6 * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/module.h>
     11#include <linux/of.h>
     12#include <linux/of_device.h>
     13
     14#include "st_thermal.h"
     15
     16/* The Thermal Framework expects millidegrees */
     17#define mcelsius(temp)			((temp) * 1000)
     18
     19/*
     20 * Function to allocate regfields which are common
     21 * between syscfg and memory mapped based sensors
     22 */
     23static int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor)
     24{
     25	struct device *dev = sensor->dev;
     26	struct regmap *regmap = sensor->regmap;
     27	const struct reg_field *reg_fields = sensor->cdata->reg_fields;
     28
     29	sensor->dcorrect = devm_regmap_field_alloc(dev, regmap,
     30						   reg_fields[DCORRECT]);
     31
     32	sensor->overflow = devm_regmap_field_alloc(dev, regmap,
     33						   reg_fields[OVERFLOW]);
     34
     35	sensor->temp_data = devm_regmap_field_alloc(dev, regmap,
     36						    reg_fields[DATA]);
     37
     38	if (IS_ERR(sensor->dcorrect) ||
     39	    IS_ERR(sensor->overflow) ||
     40	    IS_ERR(sensor->temp_data)) {
     41		dev_err(dev, "failed to allocate common regfields\n");
     42		return -EINVAL;
     43	}
     44
     45	return sensor->ops->alloc_regfields(sensor);
     46}
     47
     48static int st_thermal_sensor_on(struct st_thermal_sensor *sensor)
     49{
     50	int ret;
     51	struct device *dev = sensor->dev;
     52
     53	ret = clk_prepare_enable(sensor->clk);
     54	if (ret) {
     55		dev_err(dev, "failed to enable clk\n");
     56		return ret;
     57	}
     58
     59	ret = sensor->ops->power_ctrl(sensor, POWER_ON);
     60	if (ret) {
     61		dev_err(dev, "failed to power on sensor\n");
     62		clk_disable_unprepare(sensor->clk);
     63	}
     64
     65	return ret;
     66}
     67
     68static int st_thermal_sensor_off(struct st_thermal_sensor *sensor)
     69{
     70	int ret;
     71
     72	ret = sensor->ops->power_ctrl(sensor, POWER_OFF);
     73	if (ret)
     74		return ret;
     75
     76	clk_disable_unprepare(sensor->clk);
     77
     78	return 0;
     79}
     80
     81static int st_thermal_calibration(struct st_thermal_sensor *sensor)
     82{
     83	int ret;
     84	unsigned int val;
     85	struct device *dev = sensor->dev;
     86
     87	/* Check if sensor calibration data is already written */
     88	ret = regmap_field_read(sensor->dcorrect, &val);
     89	if (ret) {
     90		dev_err(dev, "failed to read calibration data\n");
     91		return ret;
     92	}
     93
     94	if (!val) {
     95		/*
     96		 * Sensor calibration value not set by bootloader,
     97		 * default calibration data to be used
     98		 */
     99		ret = regmap_field_write(sensor->dcorrect,
    100					 sensor->cdata->calibration_val);
    101		if (ret)
    102			dev_err(dev, "failed to set calibration data\n");
    103	}
    104
    105	return ret;
    106}
    107
    108/* Callback to get temperature from HW*/
    109static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
    110{
    111	struct st_thermal_sensor *sensor = th->devdata;
    112	struct device *dev = sensor->dev;
    113	unsigned int temp;
    114	unsigned int overflow;
    115	int ret;
    116
    117	ret = regmap_field_read(sensor->overflow, &overflow);
    118	if (ret)
    119		return ret;
    120	if (overflow)
    121		return -EIO;
    122
    123	ret = regmap_field_read(sensor->temp_data, &temp);
    124	if (ret)
    125		return ret;
    126
    127	temp += sensor->cdata->temp_adjust_val;
    128	temp = mcelsius(temp);
    129
    130	dev_dbg(dev, "temperature: %d\n", temp);
    131
    132	*temperature = temp;
    133
    134	return 0;
    135}
    136
    137static int st_thermal_get_trip_type(struct thermal_zone_device *th,
    138				int trip, enum thermal_trip_type *type)
    139{
    140	struct st_thermal_sensor *sensor = th->devdata;
    141	struct device *dev = sensor->dev;
    142
    143	switch (trip) {
    144	case 0:
    145		*type = THERMAL_TRIP_CRITICAL;
    146		break;
    147	default:
    148		dev_err(dev, "invalid trip point\n");
    149		return -EINVAL;
    150	}
    151
    152	return 0;
    153}
    154
    155static int st_thermal_get_trip_temp(struct thermal_zone_device *th,
    156				    int trip, int *temp)
    157{
    158	struct st_thermal_sensor *sensor = th->devdata;
    159	struct device *dev = sensor->dev;
    160
    161	switch (trip) {
    162	case 0:
    163		*temp = mcelsius(sensor->cdata->crit_temp);
    164		break;
    165	default:
    166		dev_err(dev, "Invalid trip point\n");
    167		return -EINVAL;
    168	}
    169
    170	return 0;
    171}
    172
    173static struct thermal_zone_device_ops st_tz_ops = {
    174	.get_temp	= st_thermal_get_temp,
    175	.get_trip_type	= st_thermal_get_trip_type,
    176	.get_trip_temp	= st_thermal_get_trip_temp,
    177};
    178
    179int st_thermal_register(struct platform_device *pdev,
    180			const struct of_device_id *st_thermal_of_match)
    181{
    182	struct st_thermal_sensor *sensor;
    183	struct device *dev = &pdev->dev;
    184	struct device_node *np = dev->of_node;
    185	const struct of_device_id *match;
    186
    187	int polling_delay;
    188	int ret;
    189
    190	if (!np) {
    191		dev_err(dev, "device tree node not found\n");
    192		return -EINVAL;
    193	}
    194
    195	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
    196	if (!sensor)
    197		return -ENOMEM;
    198
    199	sensor->dev = dev;
    200
    201	match = of_match_device(st_thermal_of_match, dev);
    202	if (!(match && match->data))
    203		return -EINVAL;
    204
    205	sensor->cdata = match->data;
    206	if (!sensor->cdata->ops)
    207		return -EINVAL;
    208
    209	sensor->ops = sensor->cdata->ops;
    210
    211	ret = (sensor->ops->regmap_init)(sensor);
    212	if (ret)
    213		return ret;
    214
    215	ret = st_thermal_alloc_regfields(sensor);
    216	if (ret)
    217		return ret;
    218
    219	sensor->clk = devm_clk_get(dev, "thermal");
    220	if (IS_ERR(sensor->clk)) {
    221		dev_err(dev, "failed to fetch clock\n");
    222		return PTR_ERR(sensor->clk);
    223	}
    224
    225	if (sensor->ops->register_enable_irq) {
    226		ret = sensor->ops->register_enable_irq(sensor);
    227		if (ret)
    228			return ret;
    229	}
    230
    231	ret = st_thermal_sensor_on(sensor);
    232	if (ret)
    233		return ret;
    234
    235	ret = st_thermal_calibration(sensor);
    236	if (ret)
    237		goto sensor_off;
    238
    239	polling_delay = sensor->ops->register_enable_irq ? 0 : 1000;
    240
    241	sensor->thermal_dev =
    242		thermal_zone_device_register(dev_name(dev), 1, 0, sensor,
    243					     &st_tz_ops, NULL, 0, polling_delay);
    244	if (IS_ERR(sensor->thermal_dev)) {
    245		dev_err(dev, "failed to register thermal zone device\n");
    246		ret = PTR_ERR(sensor->thermal_dev);
    247		goto sensor_off;
    248	}
    249	ret = thermal_zone_device_enable(sensor->thermal_dev);
    250	if (ret)
    251		goto tzd_unregister;
    252
    253	platform_set_drvdata(pdev, sensor);
    254
    255	return 0;
    256
    257tzd_unregister:
    258	thermal_zone_device_unregister(sensor->thermal_dev);
    259sensor_off:
    260	st_thermal_sensor_off(sensor);
    261
    262	return ret;
    263}
    264EXPORT_SYMBOL_GPL(st_thermal_register);
    265
    266int st_thermal_unregister(struct platform_device *pdev)
    267{
    268	struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
    269
    270	st_thermal_sensor_off(sensor);
    271	thermal_zone_device_unregister(sensor->thermal_dev);
    272
    273	return 0;
    274}
    275EXPORT_SYMBOL_GPL(st_thermal_unregister);
    276
    277#ifdef CONFIG_PM_SLEEP
    278static int st_thermal_suspend(struct device *dev)
    279{
    280	struct st_thermal_sensor *sensor = dev_get_drvdata(dev);
    281
    282	return st_thermal_sensor_off(sensor);
    283}
    284
    285static int st_thermal_resume(struct device *dev)
    286{
    287	int ret;
    288	struct st_thermal_sensor *sensor = dev_get_drvdata(dev);
    289
    290	ret = st_thermal_sensor_on(sensor);
    291	if (ret)
    292		return ret;
    293
    294	ret = st_thermal_calibration(sensor);
    295	if (ret)
    296		return ret;
    297
    298	if (sensor->ops->enable_irq) {
    299		ret = sensor->ops->enable_irq(sensor);
    300		if (ret)
    301			return ret;
    302	}
    303
    304	return 0;
    305}
    306#endif
    307
    308SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
    309EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
    310
    311MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
    312MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
    313MODULE_LICENSE("GPL v2");