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

da9062-thermal.c (7886B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Thermal device driver for DA9062 and DA9061
      4 * Copyright (C) 2017  Dialog Semiconductor
      5 */
      6
      7/* When over-temperature is reached, an interrupt from the device will be
      8 * triggered. Following this event the interrupt will be disabled and
      9 * periodic transmission of uevents (HOT trip point) should define the
     10 * first level of temperature supervision. It is expected that any final
     11 * implementation of the thermal driver will include a .notify() function
     12 * to implement these uevents to userspace.
     13 *
     14 * These uevents are intended to indicate non-invasive temperature control
     15 * of the system, where the necessary measures for cooling are the
     16 * responsibility of the host software. Once the temperature falls again,
     17 * the IRQ is re-enabled so the start of a new over-temperature event can
     18 * be detected without constant software monitoring.
     19 */
     20
     21#include <linux/errno.h>
     22#include <linux/interrupt.h>
     23#include <linux/module.h>
     24#include <linux/of.h>
     25#include <linux/platform_device.h>
     26#include <linux/regmap.h>
     27#include <linux/thermal.h>
     28#include <linux/workqueue.h>
     29
     30#include <linux/mfd/da9062/core.h>
     31#include <linux/mfd/da9062/registers.h>
     32
     33/* Minimum, maximum and default polling millisecond periods are provided
     34 * here as an example. It is expected that any final implementation to also
     35 * include a modification of these settings to match the required
     36 * application.
     37 */
     38#define DA9062_DEFAULT_POLLING_MS_PERIOD	3000
     39#define DA9062_MAX_POLLING_MS_PERIOD		10000
     40#define DA9062_MIN_POLLING_MS_PERIOD		1000
     41
     42#define DA9062_MILLI_CELSIUS(t)			((t) * 1000)
     43
     44struct da9062_thermal_config {
     45	const char *name;
     46};
     47
     48struct da9062_thermal {
     49	struct da9062 *hw;
     50	struct delayed_work work;
     51	struct thermal_zone_device *zone;
     52	struct mutex lock; /* protection for da9062_thermal temperature */
     53	int temperature;
     54	int irq;
     55	const struct da9062_thermal_config *config;
     56	struct device *dev;
     57};
     58
     59static void da9062_thermal_poll_on(struct work_struct *work)
     60{
     61	struct da9062_thermal *thermal = container_of(work,
     62						struct da9062_thermal,
     63						work.work);
     64	unsigned long delay;
     65	unsigned int val;
     66	int ret;
     67
     68	/* clear E_TEMP */
     69	ret = regmap_write(thermal->hw->regmap,
     70			   DA9062AA_EVENT_B,
     71			   DA9062AA_E_TEMP_MASK);
     72	if (ret < 0) {
     73		dev_err(thermal->dev,
     74			"Cannot clear the TJUNC temperature status\n");
     75		goto err_enable_irq;
     76	}
     77
     78	/* Now read E_TEMP again: it is acting like a status bit.
     79	 * If over-temperature, then this status will be true.
     80	 * If not over-temperature, this status will be false.
     81	 */
     82	ret = regmap_read(thermal->hw->regmap,
     83			  DA9062AA_EVENT_B,
     84			  &val);
     85	if (ret < 0) {
     86		dev_err(thermal->dev,
     87			"Cannot check the TJUNC temperature status\n");
     88		goto err_enable_irq;
     89	}
     90
     91	if (val & DA9062AA_E_TEMP_MASK) {
     92		mutex_lock(&thermal->lock);
     93		thermal->temperature = DA9062_MILLI_CELSIUS(125);
     94		mutex_unlock(&thermal->lock);
     95		thermal_zone_device_update(thermal->zone,
     96					   THERMAL_EVENT_UNSPECIFIED);
     97
     98		delay = thermal->zone->passive_delay_jiffies;
     99		queue_delayed_work(system_freezable_wq, &thermal->work, delay);
    100		return;
    101	}
    102
    103	mutex_lock(&thermal->lock);
    104	thermal->temperature = DA9062_MILLI_CELSIUS(0);
    105	mutex_unlock(&thermal->lock);
    106	thermal_zone_device_update(thermal->zone,
    107				   THERMAL_EVENT_UNSPECIFIED);
    108
    109err_enable_irq:
    110	enable_irq(thermal->irq);
    111}
    112
    113static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
    114{
    115	struct da9062_thermal *thermal = data;
    116
    117	disable_irq_nosync(thermal->irq);
    118	queue_delayed_work(system_freezable_wq, &thermal->work, 0);
    119
    120	return IRQ_HANDLED;
    121}
    122
    123static int da9062_thermal_get_trip_type(struct thermal_zone_device *z,
    124					int trip,
    125					enum thermal_trip_type *type)
    126{
    127	struct da9062_thermal *thermal = z->devdata;
    128
    129	switch (trip) {
    130	case 0:
    131		*type = THERMAL_TRIP_HOT;
    132		break;
    133	default:
    134		dev_err(thermal->dev,
    135			"Driver does not support more than 1 trip-wire\n");
    136		return -EINVAL;
    137	}
    138
    139	return 0;
    140}
    141
    142static int da9062_thermal_get_trip_temp(struct thermal_zone_device *z,
    143					int trip,
    144					int *temp)
    145{
    146	struct da9062_thermal *thermal = z->devdata;
    147
    148	switch (trip) {
    149	case 0:
    150		*temp = DA9062_MILLI_CELSIUS(125);
    151		break;
    152	default:
    153		dev_err(thermal->dev,
    154			"Driver does not support more than 1 trip-wire\n");
    155		return -EINVAL;
    156	}
    157
    158	return 0;
    159}
    160
    161static int da9062_thermal_get_temp(struct thermal_zone_device *z,
    162				   int *temp)
    163{
    164	struct da9062_thermal *thermal = z->devdata;
    165
    166	mutex_lock(&thermal->lock);
    167	*temp = thermal->temperature;
    168	mutex_unlock(&thermal->lock);
    169
    170	return 0;
    171}
    172
    173static struct thermal_zone_device_ops da9062_thermal_ops = {
    174	.get_temp	= da9062_thermal_get_temp,
    175	.get_trip_type	= da9062_thermal_get_trip_type,
    176	.get_trip_temp	= da9062_thermal_get_trip_temp,
    177};
    178
    179static const struct da9062_thermal_config da9062_config = {
    180	.name = "da9062-thermal",
    181};
    182
    183static const struct of_device_id da9062_compatible_reg_id_table[] = {
    184	{ .compatible = "dlg,da9062-thermal", .data = &da9062_config },
    185	{ },
    186};
    187
    188MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table);
    189
    190static int da9062_thermal_probe(struct platform_device *pdev)
    191{
    192	struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
    193	struct da9062_thermal *thermal;
    194	unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
    195	const struct of_device_id *match;
    196	int ret = 0;
    197
    198	match = of_match_node(da9062_compatible_reg_id_table,
    199			      pdev->dev.of_node);
    200	if (!match)
    201		return -ENXIO;
    202
    203	if (pdev->dev.of_node) {
    204		if (!of_property_read_u32(pdev->dev.of_node,
    205					  "polling-delay-passive",
    206					  &pp_tmp)) {
    207			if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD ||
    208			    pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) {
    209				dev_warn(&pdev->dev,
    210					 "Out-of-range polling period %d ms\n",
    211					 pp_tmp);
    212				pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD;
    213			}
    214		}
    215	}
    216
    217	thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal),
    218			       GFP_KERNEL);
    219	if (!thermal) {
    220		ret = -ENOMEM;
    221		goto err;
    222	}
    223
    224	thermal->config = match->data;
    225	thermal->hw = chip;
    226	thermal->dev = &pdev->dev;
    227
    228	INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on);
    229	mutex_init(&thermal->lock);
    230
    231	thermal->zone = thermal_zone_device_register(thermal->config->name,
    232					1, 0, thermal,
    233					&da9062_thermal_ops, NULL, pp_tmp,
    234					0);
    235	if (IS_ERR(thermal->zone)) {
    236		dev_err(&pdev->dev, "Cannot register thermal zone device\n");
    237		ret = PTR_ERR(thermal->zone);
    238		goto err;
    239	}
    240	ret = thermal_zone_device_enable(thermal->zone);
    241	if (ret) {
    242		dev_err(&pdev->dev, "Cannot enable thermal zone device\n");
    243		goto err_zone;
    244	}
    245
    246	dev_dbg(&pdev->dev,
    247		"TJUNC temperature polling period set at %d ms\n",
    248		jiffies_to_msecs(thermal->zone->passive_delay_jiffies));
    249
    250	ret = platform_get_irq_byname(pdev, "THERMAL");
    251	if (ret < 0) {
    252		dev_err(&pdev->dev, "Failed to get platform IRQ.\n");
    253		goto err_zone;
    254	}
    255	thermal->irq = ret;
    256
    257	ret = request_threaded_irq(thermal->irq, NULL,
    258				   da9062_thermal_irq_handler,
    259				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
    260				   "THERMAL", thermal);
    261	if (ret) {
    262		dev_err(&pdev->dev,
    263			"Failed to request thermal device IRQ.\n");
    264		goto err_zone;
    265	}
    266
    267	platform_set_drvdata(pdev, thermal);
    268	return 0;
    269
    270err_zone:
    271	thermal_zone_device_unregister(thermal->zone);
    272err:
    273	return ret;
    274}
    275
    276static int da9062_thermal_remove(struct platform_device *pdev)
    277{
    278	struct	da9062_thermal *thermal = platform_get_drvdata(pdev);
    279
    280	free_irq(thermal->irq, thermal);
    281	cancel_delayed_work_sync(&thermal->work);
    282	thermal_zone_device_unregister(thermal->zone);
    283	return 0;
    284}
    285
    286static struct platform_driver da9062_thermal_driver = {
    287	.probe	= da9062_thermal_probe,
    288	.remove	= da9062_thermal_remove,
    289	.driver	= {
    290		.name	= "da9062-thermal",
    291		.of_match_table = da9062_compatible_reg_id_table,
    292	},
    293};
    294
    295module_platform_driver(da9062_thermal_driver);
    296
    297MODULE_AUTHOR("Steve Twiss");
    298MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061");
    299MODULE_LICENSE("GPL");
    300MODULE_ALIAS("platform:da9062-thermal");