cregit-Linux how code gets into the kernel

Release 4.14 drivers/thermal/da9062-thermal.c

Directory: drivers/thermal
/*
 * Thermal device driver for DA9062 and DA9061
 * Copyright (C) 2017  Dialog Semiconductor
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/* When over-temperature is reached, an interrupt from the device will be
 * triggered. Following this event the interrupt will be disabled and
 * periodic transmission of uevents (HOT trip point) should define the
 * first level of temperature supervision. It is expected that any final
 * implementation of the thermal driver will include a .notify() function
 * to implement these uevents to userspace.
 *
 * These uevents are intended to indicate non-invasive temperature control
 * of the system, where the necessary measures for cooling are the
 * responsibility of the host software. Once the temperature falls again,
 * the IRQ is re-enabled so the start of a new over-temperature event can
 * be detected without constant software monitoring.
 */

#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/workqueue.h>

#include <linux/mfd/da9062/core.h>
#include <linux/mfd/da9062/registers.h>

/* Minimum, maximum and default polling millisecond periods are provided
 * here as an example. It is expected that any final implementation to also
 * include a modification of these settings to match the required
 * application.
 */

#define DA9062_DEFAULT_POLLING_MS_PERIOD	3000

#define DA9062_MAX_POLLING_MS_PERIOD		10000

#define DA9062_MIN_POLLING_MS_PERIOD		1000


#define DA9062_MILLI_CELSIUS(t)			((t) * 1000)


struct da9062_thermal_config {
	
const char *name;
};


struct da9062_thermal {
	
struct da9062 *hw;
	
struct delayed_work work;
	
struct thermal_zone_device *zone;
	
enum thermal_device_mode mode;
	
struct mutex lock; /* protection for da9062_thermal temperature */
	
int temperature;
	
int irq;
	
const struct da9062_thermal_config *config;
	
struct device *dev;
};


static void da9062_thermal_poll_on(struct work_struct *work) { struct da9062_thermal *thermal = container_of(work, struct da9062_thermal, work.work); unsigned long delay; unsigned int val; int ret; /* clear E_TEMP */ ret = regmap_write(thermal->hw->regmap, DA9062AA_EVENT_B, DA9062AA_E_TEMP_MASK); if (ret < 0) { dev_err(thermal->dev, "Cannot clear the TJUNC temperature status\n"); goto err_enable_irq; } /* Now read E_TEMP again: it is acting like a status bit. * If over-temperature, then this status will be true. * If not over-temperature, this status will be false. */ ret = regmap_read(thermal->hw->regmap, DA9062AA_EVENT_B, &val); if (ret < 0) { dev_err(thermal->dev, "Cannot check the TJUNC temperature status\n"); goto err_enable_irq; } if (val & DA9062AA_E_TEMP_MASK) { mutex_lock(&thermal->lock); thermal->temperature = DA9062_MILLI_CELSIUS(125); mutex_unlock(&thermal->lock); thermal_zone_device_update(thermal->zone, THERMAL_EVENT_UNSPECIFIED); delay = msecs_to_jiffies(thermal->zone->passive_delay); schedule_delayed_work(&thermal->work, delay); return; } mutex_lock(&thermal->lock); thermal->temperature = DA9062_MILLI_CELSIUS(0); mutex_unlock(&thermal->lock); thermal_zone_device_update(thermal->zone, THERMAL_EVENT_UNSPECIFIED); err_enable_irq: enable_irq(thermal->irq); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss219100.00%1100.00%
Total219100.00%1100.00%


static irqreturn_t da9062_thermal_irq_handler(int irq, void *data) { struct da9062_thermal *thermal = data; disable_irq_nosync(thermal->irq); schedule_delayed_work(&thermal->work, 0); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss40100.00%1100.00%
Total40100.00%1100.00%


static int da9062_thermal_get_mode(struct thermal_zone_device *z, enum thermal_device_mode *mode) { struct da9062_thermal *thermal = z->devdata; *mode = thermal->mode; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss35100.00%1100.00%
Total35100.00%1100.00%


static int da9062_thermal_get_trip_type(struct thermal_zone_device *z, int trip, enum thermal_trip_type *type) { struct da9062_thermal *thermal = z->devdata; switch (trip) { case 0: *type = THERMAL_TRIP_HOT; break; default: dev_err(thermal->dev, "Driver does not support more than 1 trip-wire\n"); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss60100.00%1100.00%
Total60100.00%1100.00%


static int da9062_thermal_get_trip_temp(struct thermal_zone_device *z, int trip, int *temp) { struct da9062_thermal *thermal = z->devdata; switch (trip) { case 0: *temp = DA9062_MILLI_CELSIUS(125); break; default: dev_err(thermal->dev, "Driver does not support more than 1 trip-wire\n"); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss62100.00%1100.00%
Total62100.00%1100.00%


static int da9062_thermal_get_temp(struct thermal_zone_device *z, int *temp) { struct da9062_thermal *thermal = z->devdata; mutex_lock(&thermal->lock); *temp = thermal->temperature; mutex_unlock(&thermal->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss50100.00%1100.00%
Total50100.00%1100.00%

static struct thermal_zone_device_ops da9062_thermal_ops = { .get_temp = da9062_thermal_get_temp, .get_mode = da9062_thermal_get_mode, .get_trip_type = da9062_thermal_get_trip_type, .get_trip_temp = da9062_thermal_get_trip_temp, }; static const struct da9062_thermal_config da9062_config = { .name = "da9062-thermal", }; static const struct of_device_id da9062_compatible_reg_id_table[] = { { .compatible = "dlg,da9062-thermal", .data = &da9062_config }, { }, }; MODULE_DEVICE_TABLE(of, da9062_compatible_reg_id_table);
static int da9062_thermal_probe(struct platform_device *pdev) { struct da9062 *chip = dev_get_drvdata(pdev->dev.parent); struct da9062_thermal *thermal; unsigned int pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; const struct of_device_id *match; int ret = 0; match = of_match_node(da9062_compatible_reg_id_table, pdev->dev.of_node); if (!match) return -ENXIO; if (pdev->dev.of_node) { if (!of_property_read_u32(pdev->dev.of_node, "polling-delay-passive", &pp_tmp)) { if (pp_tmp < DA9062_MIN_POLLING_MS_PERIOD || pp_tmp > DA9062_MAX_POLLING_MS_PERIOD) { dev_warn(&pdev->dev, "Out-of-range polling period %d ms\n", pp_tmp); pp_tmp = DA9062_DEFAULT_POLLING_MS_PERIOD; } } } thermal = devm_kzalloc(&pdev->dev, sizeof(struct da9062_thermal), GFP_KERNEL); if (!thermal) { ret = -ENOMEM; goto err; } thermal->config = match->data; thermal->hw = chip; thermal->mode = THERMAL_DEVICE_ENABLED; thermal->dev = &pdev->dev; INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on); mutex_init(&thermal->lock); thermal->zone = thermal_zone_device_register(thermal->config->name, 1, 0, thermal, &da9062_thermal_ops, NULL, pp_tmp, 0); if (IS_ERR(thermal->zone)) { dev_err(&pdev->dev, "Cannot register thermal zone device\n"); ret = PTR_ERR(thermal->zone); goto err; } dev_dbg(&pdev->dev, "TJUNC temperature polling period set at %d ms\n", thermal->zone->passive_delay); ret = platform_get_irq_byname(pdev, "THERMAL"); if (ret < 0) { dev_err(&pdev->dev, "Failed to get platform IRQ.\n"); goto err_zone; } thermal->irq = ret; ret = request_threaded_irq(thermal->irq, NULL, da9062_thermal_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "THERMAL", thermal); if (ret) { dev_err(&pdev->dev, "Failed to request thermal device IRQ.\n"); goto err_zone; } platform_set_drvdata(pdev, thermal); return 0; err_zone: thermal_zone_device_unregister(thermal->zone); err: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss383100.00%1100.00%
Total383100.00%1100.00%


static int da9062_thermal_remove(struct platform_device *pdev) { struct da9062_thermal *thermal = platform_get_drvdata(pdev); free_irq(thermal->irq, thermal); cancel_delayed_work_sync(&thermal->work); thermal_zone_device_unregister(thermal->zone); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss48100.00%1100.00%
Total48100.00%1100.00%

static struct platform_driver da9062_thermal_driver = { .probe = da9062_thermal_probe, .remove = da9062_thermal_remove, .driver = { .name = "da9062-thermal", .of_match_table = da9062_compatible_reg_id_table, }, }; module_platform_driver(da9062_thermal_driver); MODULE_AUTHOR("Steve Twiss"); MODULE_DESCRIPTION("Thermal TJUNC device driver for Dialog DA9062 and DA9061"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:da9062-thermal");

Overall Contributors

PersonTokensPropCommitsCommitProp
Steve Twiss1132100.00%1100.00%
Total1132100.00%1100.00%
Directory: drivers/thermal
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.