mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
Merge branches 'misc', 'soc', 'soc-eduardo' and 'int3404-thermal' of .git into next
This commit is contained in:
commit
201531c277
28 changed files with 2212 additions and 67 deletions
|
|
@ -29,6 +29,19 @@ config THERMAL_HWMON
|
|||
Say 'Y' here if you want all thermal sensors to
|
||||
have hwmon sysfs interface too.
|
||||
|
||||
config THERMAL_OF
|
||||
bool
|
||||
prompt "APIs to parse thermal data out of device tree"
|
||||
depends on OF
|
||||
default y
|
||||
help
|
||||
This options provides helpers to add the support to
|
||||
read and parse thermal data definitions out of the
|
||||
device tree blob.
|
||||
|
||||
Say 'Y' here if you need to build thermal infrastructure
|
||||
based on device tree.
|
||||
|
||||
choice
|
||||
prompt "Default Thermal governor"
|
||||
default THERMAL_DEFAULT_GOV_STEP_WISE
|
||||
|
|
@ -79,6 +92,7 @@ config THERMAL_GOV_USER_SPACE
|
|||
config CPU_THERMAL
|
||||
bool "generic cpu cooling support"
|
||||
depends on CPU_FREQ
|
||||
depends on THERMAL_OF
|
||||
help
|
||||
This implements the generic cpu cooling mechanism through frequency
|
||||
reduction. An ACPI version of this already exists
|
||||
|
|
@ -121,7 +135,7 @@ config SPEAR_THERMAL
|
|||
|
||||
config RCAR_THERMAL
|
||||
tristate "Renesas R-Car thermal driver"
|
||||
depends on ARCH_SHMOBILE
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
Enable this to plug the R-Car thermal sensor driver into the Linux
|
||||
thermal framework.
|
||||
|
|
@ -192,6 +206,13 @@ config X86_PKG_TEMP_THERMAL
|
|||
two trip points which can be set by user to get notifications via thermal
|
||||
notification methods.
|
||||
|
||||
config ACPI_INT3403_THERMAL
|
||||
tristate "ACPI INT3403 thermal driver"
|
||||
depends on X86 && ACPI
|
||||
help
|
||||
This driver uses ACPI INT3403 device objects. If present, it will
|
||||
register each INT3403 thermal sensor as a thermal zone.
|
||||
|
||||
menu "Texas Instruments thermal drivers"
|
||||
source "drivers/thermal/ti-soc-thermal/Kconfig"
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ thermal_sys-y += thermal_core.o
|
|||
|
||||
# interface to/from other layers providing sensors
|
||||
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
|
||||
thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o
|
||||
|
||||
# governors
|
||||
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
|
||||
|
|
@ -29,3 +30,4 @@ obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
|
|||
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
||||
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
|
||||
|
|
|
|||
|
|
@ -424,18 +424,21 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
|
|||
};
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* __cpufreq_cooling_register - helper function to create cpufreq cooling device
|
||||
* @np: a valid struct device_node to the cooling device device tree node
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
|
||||
*
|
||||
* This interface function registers the cpufreq cooling device with the name
|
||||
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
|
||||
* cooling devices.
|
||||
* cooling devices. It also gives the opportunity to link the cooling device
|
||||
* with a device tree node, in order to bind it via the thermal DT code.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
static struct thermal_cooling_device *
|
||||
__cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus)
|
||||
{
|
||||
struct thermal_cooling_device *cool_dev;
|
||||
struct cpufreq_cooling_device *cpufreq_dev = NULL;
|
||||
|
|
@ -474,8 +477,8 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
|||
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
|
||||
cpufreq_dev->id);
|
||||
|
||||
cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
|
||||
&cpufreq_cooling_ops);
|
||||
cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
|
||||
&cpufreq_cooling_ops);
|
||||
if (IS_ERR(cool_dev)) {
|
||||
release_idr(&cpufreq_idr, cpufreq_dev->id);
|
||||
kfree(cpufreq_dev);
|
||||
|
|
@ -495,8 +498,49 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
|||
|
||||
return cool_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
|
||||
*
|
||||
* This interface function registers the cpufreq cooling device with the name
|
||||
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
|
||||
* cooling devices.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
{
|
||||
return __cpufreq_cooling_register(NULL, clip_cpus);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
|
||||
|
||||
/**
|
||||
* of_cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* @np: a valid struct device_node to the cooling device device tree node
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
|
||||
*
|
||||
* This interface function registers the cpufreq cooling device with the name
|
||||
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
|
||||
* cooling devices. Using this API, the cpufreq cooling device will be
|
||||
* linked to the device tree node provided.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
of_cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus)
|
||||
{
|
||||
if (!np)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return __cpufreq_cooling_register(np, clip_cpus);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_unregister - function to remove cpufreq cooling device.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
|
|
|
|||
|
|
@ -490,27 +490,30 @@ static int imx_thermal_suspend(struct device *dev)
|
|||
{
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
u32 val;
|
||||
|
||||
regmap_read(map, TEMPSENSE0, &val);
|
||||
if ((val & TEMPSENSE0_POWER_DOWN) == 0) {
|
||||
/*
|
||||
* If a measurement is taking place, wait for a long enough
|
||||
* time for it to finish, and then check again. If it still
|
||||
* does not finish, something must go wrong.
|
||||
*/
|
||||
udelay(50);
|
||||
regmap_read(map, TEMPSENSE0, &val);
|
||||
if ((val & TEMPSENSE0_POWER_DOWN) == 0)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
/*
|
||||
* Need to disable thermal sensor, otherwise, when thermal core
|
||||
* try to get temperature before thermal sensor resume, a wrong
|
||||
* temperature will be read as the thermal sensor is powered
|
||||
* down.
|
||||
*/
|
||||
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
|
||||
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
|
||||
data->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_thermal_resume(struct device *dev)
|
||||
{
|
||||
/* Nothing to do for now */
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
|
||||
/* Enabled thermal sensor after resume */
|
||||
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
|
||||
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
|
||||
data->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -522,6 +525,7 @@ static const struct of_device_id of_imx_thermal_match[] = {
|
|||
{ .compatible = "fsl,imx6q-tempmon", },
|
||||
{ /* end */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
|
||||
|
||||
static struct platform_driver imx_thermal = {
|
||||
.driver = {
|
||||
|
|
|
|||
237
drivers/thermal/int3403_thermal.c
Normal file
237
drivers/thermal/int3403_thermal.c
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* ACPI INT3403 thermal driver
|
||||
* Copyright (c) 2013, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define INT3403_TYPE_SENSOR 0x03
|
||||
#define INT3403_PERF_CHANGED_EVENT 0x80
|
||||
#define INT3403_THERMAL_EVENT 0x90
|
||||
|
||||
#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
|
||||
#define KELVIN_OFFSET 2732
|
||||
#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
|
||||
|
||||
#define ACPI_INT3403_CLASS "int3403"
|
||||
#define ACPI_INT3403_FILE_STATE "state"
|
||||
|
||||
struct int3403_sensor {
|
||||
struct thermal_zone_device *tzone;
|
||||
unsigned long *thresholds;
|
||||
};
|
||||
|
||||
static int sys_get_curr_temp(struct thermal_zone_device *tzone,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
unsigned long long hyst;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
/*
|
||||
* get_trip_temp is a mandatory callback but
|
||||
* PATx method doesn't return any value, so return
|
||||
* cached value, which was last set from user space.
|
||||
*/
|
||||
*temp = obj->thresholds[trip];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
/* Mandatory callback, may not mean much here */
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
int ret = 0;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
if (acpi_has_method(device->handle, name)) {
|
||||
status = acpi_execute_simple_method(device->handle, name,
|
||||
MILLI_CELSIUS_TO_DECI_KELVIN(temp,
|
||||
KELVIN_OFFSET));
|
||||
if (ACPI_FAILURE(status))
|
||||
ret = -EIO;
|
||||
else
|
||||
obj->thresholds[trip] = temp;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
.get_trip_hyst = sys_get_trip_hyst,
|
||||
};
|
||||
|
||||
static void acpi_thermal_notify(struct acpi_device *device, u32 event)
|
||||
{
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
obj = acpi_driver_data(device);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case INT3403_PERF_CHANGED_EVENT:
|
||||
break;
|
||||
case INT3403_THERMAL_EVENT:
|
||||
thermal_zone_device_update(obj->tzone);
|
||||
break;
|
||||
default:
|
||||
dev_err(&device->dev, "Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int acpi_int3403_add(struct acpi_device *device)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned long long ptyp;
|
||||
acpi_status status;
|
||||
struct int3403_sensor *obj;
|
||||
unsigned long long trip_cnt;
|
||||
int trip_mask = 0;
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EINVAL;
|
||||
|
||||
if (ptyp != INT3403_TYPE_SENSOR)
|
||||
return -EINVAL;
|
||||
|
||||
obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
device->driver_data = obj;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "PATC", NULL,
|
||||
&trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
|
||||
if (trip_cnt) {
|
||||
/* We have to cache, thresholds can't be readback */
|
||||
obj->thresholds = devm_kzalloc(&device->dev,
|
||||
sizeof(*obj->thresholds) * trip_cnt,
|
||||
GFP_KERNEL);
|
||||
if (!obj->thresholds)
|
||||
return -ENOMEM;
|
||||
trip_mask = BIT(trip_cnt) - 1;
|
||||
}
|
||||
obj->tzone = thermal_zone_device_register(acpi_device_bid(device),
|
||||
trip_cnt, trip_mask, device, &tzone_ops,
|
||||
NULL, 0, 0);
|
||||
if (IS_ERR(obj->tzone)) {
|
||||
result = PTR_ERR(obj->tzone);
|
||||
return result;
|
||||
}
|
||||
|
||||
strcpy(acpi_device_name(device), "INT3403");
|
||||
strcpy(acpi_device_class(device), ACPI_INT3403_CLASS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_int3403_remove(struct acpi_device *device)
|
||||
{
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
obj = acpi_driver_data(device);
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ACPI_MODULE_NAME("int3403");
|
||||
static const struct acpi_device_id int3403_device_ids[] = {
|
||||
{"INT3403", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
|
||||
|
||||
static struct acpi_driver acpi_int3403_driver = {
|
||||
.name = "INT3403",
|
||||
.class = ACPI_INT3403_CLASS,
|
||||
.ids = int3403_device_ids,
|
||||
.ops = {
|
||||
.add = acpi_int3403_add,
|
||||
.remove = acpi_int3403_remove,
|
||||
.notify = acpi_thermal_notify,
|
||||
},
|
||||
};
|
||||
|
||||
module_acpi_driver(acpi_int3403_driver);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
|
||||
849
drivers/thermal/of-thermal.c
Normal file
849
drivers/thermal/of-thermal.c
Normal file
|
|
@ -0,0 +1,849 @@
|
|||
/*
|
||||
* of-thermal.c - Generic Thermal Management device tree support.
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/*** Private data structures to represent thermal device tree data ***/
|
||||
|
||||
/**
|
||||
* struct __thermal_trip - representation of a point in temperature domain
|
||||
* @np: pointer to struct device_node that this trip point was created from
|
||||
* @temperature: temperature value in miliCelsius
|
||||
* @hysteresis: relative hysteresis in miliCelsius
|
||||
* @type: trip point type
|
||||
*/
|
||||
|
||||
struct __thermal_trip {
|
||||
struct device_node *np;
|
||||
unsigned long int temperature;
|
||||
unsigned long int hysteresis;
|
||||
enum thermal_trip_type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_bind_param - a match between trip and cooling device
|
||||
* @cooling_device: a pointer to identify the referred cooling device
|
||||
* @trip_id: the trip point index
|
||||
* @usage: the percentage (from 0 to 100) of cooling contribution
|
||||
* @min: minimum cooling state used at this trip point
|
||||
* @max: maximum cooling state used at this trip point
|
||||
*/
|
||||
|
||||
struct __thermal_bind_params {
|
||||
struct device_node *cooling_device;
|
||||
unsigned int trip_id;
|
||||
unsigned int usage;
|
||||
unsigned long min;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_zone - internal representation of a thermal zone
|
||||
* @mode: current thermal zone device mode (enabled/disabled)
|
||||
* @passive_delay: polling interval while passive cooling is activated
|
||||
* @polling_delay: zone polling interval
|
||||
* @ntrips: number of trip points
|
||||
* @trips: an array of trip points (0..ntrips - 1)
|
||||
* @num_tbps: number of thermal bind params
|
||||
* @tbps: an array of thermal bind params (0..num_tbps - 1)
|
||||
* @sensor_data: sensor private data used while reading temperature and trend
|
||||
* @get_temp: sensor callback to read temperature
|
||||
* @get_trend: sensor callback to read temperature trend
|
||||
*/
|
||||
|
||||
struct __thermal_zone {
|
||||
enum thermal_device_mode mode;
|
||||
int passive_delay;
|
||||
int polling_delay;
|
||||
|
||||
/* trip data */
|
||||
int ntrips;
|
||||
struct __thermal_trip *trips;
|
||||
|
||||
/* cooling binding data */
|
||||
int num_tbps;
|
||||
struct __thermal_bind_params *tbps;
|
||||
|
||||
/* sensor interface */
|
||||
void *sensor_data;
|
||||
int (*get_temp)(void *, long *);
|
||||
int (*get_trend)(void *, long *);
|
||||
};
|
||||
|
||||
/*** DT thermal zone device callbacks ***/
|
||||
|
||||
static int of_thermal_get_temp(struct thermal_zone_device *tz,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data->get_temp)
|
||||
return -EINVAL;
|
||||
|
||||
return data->get_temp(data->sensor_data, temp);
|
||||
}
|
||||
|
||||
static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trend *trend)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
long dev_trend;
|
||||
int r;
|
||||
|
||||
if (!data->get_trend)
|
||||
return -EINVAL;
|
||||
|
||||
r = data->get_trend(data->sensor_data, &dev_trend);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* TODO: These intervals might have some thresholds, but in core code */
|
||||
if (dev_trend > 0)
|
||||
*trend = THERMAL_TREND_RAISING;
|
||||
else if (dev_trend < 0)
|
||||
*trend = THERMAL_TREND_DROPPING;
|
||||
else
|
||||
*trend = THERMAL_TREND_STABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_bind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct __thermal_zone *data = thermal->devdata;
|
||||
int i;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
/* find where to bind */
|
||||
for (i = 0; i < data->num_tbps; i++) {
|
||||
struct __thermal_bind_params *tbp = data->tbps + i;
|
||||
|
||||
if (tbp->cooling_device == cdev->np) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_zone_bind_cooling_device(thermal,
|
||||
tbp->trip_id, cdev,
|
||||
tbp->min,
|
||||
tbp->max);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_unbind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct __thermal_zone *data = thermal->devdata;
|
||||
int i;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
/* find where to unbind */
|
||||
for (i = 0; i < data->num_tbps; i++) {
|
||||
struct __thermal_bind_params *tbp = data->tbps + i;
|
||||
|
||||
if (tbp->cooling_device == cdev->np) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_zone_unbind_cooling_device(thermal,
|
||||
tbp->trip_id, cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
*mode = data->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
tz->polling_delay = data->polling_delay;
|
||||
else
|
||||
tz->polling_delay = 0;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
data->mode = mode;
|
||||
thermal_zone_device_update(tz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*type = data->trips[trip].type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*temp = data->trips[trip].temperature;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
/* thermal framework should take care of data->mask & (1 << trip) */
|
||||
data->trips[trip].temperature = temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long *hyst)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*hyst = data->trips[trip].hysteresis;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long hyst)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
/* thermal framework should take care of data->mask & (1 << trip) */
|
||||
data->trips[trip].hysteresis = hyst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data->ntrips; i++)
|
||||
if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
|
||||
*temp = data->trips[i].temperature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops of_thermal_ops = {
|
||||
.get_mode = of_thermal_get_mode,
|
||||
.set_mode = of_thermal_set_mode,
|
||||
|
||||
.get_trip_type = of_thermal_get_trip_type,
|
||||
.get_trip_temp = of_thermal_get_trip_temp,
|
||||
.set_trip_temp = of_thermal_set_trip_temp,
|
||||
.get_trip_hyst = of_thermal_get_trip_hyst,
|
||||
.set_trip_hyst = of_thermal_set_trip_hyst,
|
||||
.get_crit_temp = of_thermal_get_crit_temp,
|
||||
|
||||
.bind = of_thermal_bind,
|
||||
.unbind = of_thermal_unbind,
|
||||
};
|
||||
|
||||
/*** sensor API ***/
|
||||
|
||||
static struct thermal_zone_device *
|
||||
thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
struct device_node *sensor, void *data,
|
||||
int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
{
|
||||
struct thermal_zone_device *tzd;
|
||||
struct __thermal_zone *tz;
|
||||
|
||||
tzd = thermal_zone_get_zone_by_name(zone->name);
|
||||
if (IS_ERR(tzd))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
tz = tzd->devdata;
|
||||
|
||||
mutex_lock(&tzd->lock);
|
||||
tz->get_temp = get_temp;
|
||||
tz->get_trend = get_trend;
|
||||
tz->sensor_data = data;
|
||||
|
||||
tzd->ops->get_temp = of_thermal_get_temp;
|
||||
tzd->ops->get_trend = of_thermal_get_trend;
|
||||
mutex_unlock(&tzd->lock);
|
||||
|
||||
return tzd;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
|
||||
* @dev: a valid struct device pointer of a sensor device. Must contain
|
||||
* a valid .of_node, for the sensor node.
|
||||
* @sensor_id: a sensor identifier, in case the sensor IP has more
|
||||
* than one sensors
|
||||
* @data: a private pointer (owned by the caller) that will be passed
|
||||
* back, when a temperature reading is needed.
|
||||
* @get_temp: a pointer to a function that reads the sensor temperature.
|
||||
* @get_trend: a pointer to a function that reads the sensor temperature trend.
|
||||
*
|
||||
* This function will search the list of thermal zones described in device
|
||||
* tree and look for the zone that refer to the sensor device pointed by
|
||||
* @dev->of_node as temperature providers. For the zone pointing to the
|
||||
* sensor node, the sensor will be added to the DT thermal zone device.
|
||||
*
|
||||
* The thermal zone temperature is provided by the @get_temp function
|
||||
* pointer. When called, it will have the private pointer @data back.
|
||||
*
|
||||
* The thermal zone temperature trend is provided by the @get_trend function
|
||||
* pointer. When called, it will have the private pointer @data back.
|
||||
*
|
||||
* TODO:
|
||||
* 01 - This function must enqueue the new sensor instead of using
|
||||
* it as the only source of temperature values.
|
||||
*
|
||||
* 02 - There must be a way to match the sensor with all thermal zones
|
||||
* that refer to it.
|
||||
*
|
||||
* Return: On success returns a valid struct thermal_zone_device,
|
||||
* otherwise, it returns a corresponding ERR_PTR(). Caller must
|
||||
* check the return value with help of IS_ERR() helper.
|
||||
*/
|
||||
struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
|
||||
void *data, int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
{
|
||||
struct device_node *np, *child, *sensor_np;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (!dev || !dev->of_node)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
sensor_np = dev->of_node;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct of_phandle_args sensor_specs;
|
||||
int ret, id;
|
||||
|
||||
/* For now, thermal framework supports only 1 sensor per zone */
|
||||
ret = of_parse_phandle_with_args(child, "thermal-sensors",
|
||||
"#thermal-sensor-cells",
|
||||
0, &sensor_specs);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
if (sensor_specs.args_count >= 1) {
|
||||
id = sensor_specs.args[0];
|
||||
WARN(sensor_specs.args_count > 1,
|
||||
"%s: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np->name, sensor_specs.args_count);
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
if (sensor_specs.np == sensor_np && id == sensor_id) {
|
||||
of_node_put(np);
|
||||
return thermal_zone_of_add_sensor(child, sensor_np,
|
||||
data,
|
||||
get_temp,
|
||||
get_trend);
|
||||
}
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
|
||||
|
||||
/**
|
||||
* thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
|
||||
* @dev: a valid struct device pointer of a sensor device. Must contain
|
||||
* a valid .of_node, for the sensor node.
|
||||
* @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
|
||||
*
|
||||
* This function removes the sensor callbacks and private data from the
|
||||
* thermal zone device registered with thermal_zone_of_sensor_register()
|
||||
* API. It will also silent the zone by remove the .get_temp() and .get_trend()
|
||||
* thermal zone device callbacks.
|
||||
*
|
||||
* TODO: When the support to several sensors per zone is added, this
|
||||
* function must search the sensor list based on @dev parameter.
|
||||
*
|
||||
*/
|
||||
void thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
struct thermal_zone_device *tzd)
|
||||
{
|
||||
struct __thermal_zone *tz;
|
||||
|
||||
if (!dev || !tzd || !tzd->devdata)
|
||||
return;
|
||||
|
||||
tz = tzd->devdata;
|
||||
|
||||
/* no __thermal_zone, nothing to be done */
|
||||
if (!tz)
|
||||
return;
|
||||
|
||||
mutex_lock(&tzd->lock);
|
||||
tzd->ops->get_temp = NULL;
|
||||
tzd->ops->get_trend = NULL;
|
||||
|
||||
tz->get_temp = NULL;
|
||||
tz->get_trend = NULL;
|
||||
tz->sensor_data = NULL;
|
||||
mutex_unlock(&tzd->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
|
||||
|
||||
/*** functions parsing device tree nodes ***/
|
||||
|
||||
/**
|
||||
* thermal_of_populate_bind_params - parse and fill cooling map data
|
||||
* @np: DT node containing a cooling-map node
|
||||
* @__tbp: data structure to be filled with cooling map info
|
||||
* @trips: array of thermal zone trip points
|
||||
* @ntrips: number of trip points inside trips.
|
||||
*
|
||||
* This function parses a cooling-map type of node represented by
|
||||
* @np parameter and fills the read data into @__tbp data structure.
|
||||
* It needs the already parsed array of trip points of the thermal zone
|
||||
* in consideration.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*/
|
||||
static int thermal_of_populate_bind_params(struct device_node *np,
|
||||
struct __thermal_bind_params *__tbp,
|
||||
struct __thermal_trip *trips,
|
||||
int ntrips)
|
||||
{
|
||||
struct of_phandle_args cooling_spec;
|
||||
struct device_node *trip;
|
||||
int ret, i;
|
||||
u32 prop;
|
||||
|
||||
/* Default weight. Usage is optional */
|
||||
__tbp->usage = 0;
|
||||
ret = of_property_read_u32(np, "contribution", &prop);
|
||||
if (ret == 0)
|
||||
__tbp->usage = prop;
|
||||
|
||||
trip = of_parse_phandle(np, "trip", 0);
|
||||
if (!trip) {
|
||||
pr_err("missing trip property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* match using device_node */
|
||||
for (i = 0; i < ntrips; i++)
|
||||
if (trip == trips[i].np) {
|
||||
__tbp->trip_id = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ntrips) {
|
||||
ret = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
|
||||
0, &cooling_spec);
|
||||
if (ret < 0) {
|
||||
pr_err("missing cooling_device property\n");
|
||||
goto end;
|
||||
}
|
||||
__tbp->cooling_device = cooling_spec.np;
|
||||
if (cooling_spec.args_count >= 2) { /* at least min and max */
|
||||
__tbp->min = cooling_spec.args[0];
|
||||
__tbp->max = cooling_spec.args[1];
|
||||
} else {
|
||||
pr_err("wrong reference to cooling device, missing limits\n");
|
||||
}
|
||||
|
||||
end:
|
||||
of_node_put(trip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It maps 'enum thermal_trip_type' found in include/linux/thermal.h
|
||||
* into the device tree binding of 'trip', property type.
|
||||
*/
|
||||
static const char * const trip_types[] = {
|
||||
[THERMAL_TRIP_ACTIVE] = "active",
|
||||
[THERMAL_TRIP_PASSIVE] = "passive",
|
||||
[THERMAL_TRIP_HOT] = "hot",
|
||||
[THERMAL_TRIP_CRITICAL] = "critical",
|
||||
};
|
||||
|
||||
/**
|
||||
* thermal_of_get_trip_type - Get phy mode for given device_node
|
||||
* @np: Pointer to the given device_node
|
||||
* @type: Pointer to resulting trip type
|
||||
*
|
||||
* The function gets trip type string from property 'type',
|
||||
* and store its index in trip_types table in @type,
|
||||
*
|
||||
* Return: 0 on success, or errno in error case.
|
||||
*/
|
||||
static int thermal_of_get_trip_type(struct device_node *np,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
const char *t;
|
||||
int err, i;
|
||||
|
||||
err = of_property_read_string(np, "type", &t);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trip_types); i++)
|
||||
if (!strcasecmp(t, trip_types[i])) {
|
||||
*type = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_of_populate_trip - parse and fill one trip point data
|
||||
* @np: DT node containing a trip point node
|
||||
* @trip: trip point data structure to be filled up
|
||||
*
|
||||
* This function parses a trip point type of node represented by
|
||||
* @np parameter and fills the read data into @trip data structure.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*/
|
||||
static int thermal_of_populate_trip(struct device_node *np,
|
||||
struct __thermal_trip *trip)
|
||||
{
|
||||
int prop;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "temperature", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing temperature property\n");
|
||||
return ret;
|
||||
}
|
||||
trip->temperature = prop;
|
||||
|
||||
ret = of_property_read_u32(np, "hysteresis", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing hysteresis property\n");
|
||||
return ret;
|
||||
}
|
||||
trip->hysteresis = prop;
|
||||
|
||||
ret = thermal_of_get_trip_type(np, &trip->type);
|
||||
if (ret < 0) {
|
||||
pr_err("wrong trip type property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Required for cooling map matching */
|
||||
trip->np = np;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_of_build_thermal_zone - parse and fill one thermal zone data
|
||||
* @np: DT node containing a thermal zone node
|
||||
*
|
||||
* This function parses a thermal zone type of node represented by
|
||||
* @np parameter and fills the read data into a __thermal_zone data structure
|
||||
* and return this pointer.
|
||||
*
|
||||
* TODO: Missing properties to parse: thermal-sensor-names and coefficients
|
||||
*
|
||||
* Return: On success returns a valid struct __thermal_zone,
|
||||
* otherwise, it returns a corresponding ERR_PTR(). Caller must
|
||||
* check the return value with help of IS_ERR() helper.
|
||||
*/
|
||||
static struct __thermal_zone *
|
||||
thermal_of_build_thermal_zone(struct device_node *np)
|
||||
{
|
||||
struct device_node *child = NULL, *gchild;
|
||||
struct __thermal_zone *tz;
|
||||
int ret, i;
|
||||
u32 prop;
|
||||
|
||||
if (!np) {
|
||||
pr_err("no thermal zone np\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
tz = kzalloc(sizeof(*tz), GFP_KERNEL);
|
||||
if (!tz)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = of_property_read_u32(np, "polling-delay-passive", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing polling-delay-passive property\n");
|
||||
goto free_tz;
|
||||
}
|
||||
tz->passive_delay = prop;
|
||||
|
||||
ret = of_property_read_u32(np, "polling-delay", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing polling-delay property\n");
|
||||
goto free_tz;
|
||||
}
|
||||
tz->polling_delay = prop;
|
||||
|
||||
/* trips */
|
||||
child = of_get_child_by_name(np, "trips");
|
||||
|
||||
/* No trips provided */
|
||||
if (!child)
|
||||
goto finish;
|
||||
|
||||
tz->ntrips = of_get_child_count(child);
|
||||
if (tz->ntrips == 0) /* must have at least one child */
|
||||
goto finish;
|
||||
|
||||
tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
|
||||
if (!tz->trips) {
|
||||
ret = -ENOMEM;
|
||||
goto free_tz;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(child, gchild) {
|
||||
ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
|
||||
if (ret)
|
||||
goto free_trips;
|
||||
}
|
||||
|
||||
of_node_put(child);
|
||||
|
||||
/* cooling-maps */
|
||||
child = of_get_child_by_name(np, "cooling-maps");
|
||||
|
||||
/* cooling-maps not provided */
|
||||
if (!child)
|
||||
goto finish;
|
||||
|
||||
tz->num_tbps = of_get_child_count(child);
|
||||
if (tz->num_tbps == 0)
|
||||
goto finish;
|
||||
|
||||
tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
|
||||
if (!tz->tbps) {
|
||||
ret = -ENOMEM;
|
||||
goto free_trips;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(child, gchild)
|
||||
ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
|
||||
tz->trips, tz->ntrips);
|
||||
if (ret)
|
||||
goto free_tbps;
|
||||
|
||||
finish:
|
||||
of_node_put(child);
|
||||
tz->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return tz;
|
||||
|
||||
free_tbps:
|
||||
kfree(tz->tbps);
|
||||
free_trips:
|
||||
kfree(tz->trips);
|
||||
free_tz:
|
||||
kfree(tz);
|
||||
of_node_put(child);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static inline void of_thermal_free_zone(struct __thermal_zone *tz)
|
||||
{
|
||||
kfree(tz->tbps);
|
||||
kfree(tz->trips);
|
||||
kfree(tz);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_parse_thermal_zones - parse device tree thermal data
|
||||
*
|
||||
* Initialization function that can be called by machine initialization
|
||||
* code to parse thermal data and populate the thermal framework
|
||||
* with hardware thermal zones info. This function only parses thermal zones.
|
||||
* Cooling devices and sensor devices nodes are supposed to be parsed
|
||||
* by their respective drivers.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*
|
||||
*/
|
||||
int __init of_parse_thermal_zones(void)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
struct __thermal_zone *tz;
|
||||
struct thermal_zone_device_ops *ops;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np) {
|
||||
pr_debug("unable to find thermal zones\n");
|
||||
return 0; /* Run successfully on systems without thermal DT */
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct thermal_zone_device *zone;
|
||||
struct thermal_zone_params *tzp;
|
||||
|
||||
tz = thermal_of_build_thermal_zone(child);
|
||||
if (IS_ERR(tz)) {
|
||||
pr_err("failed to build thermal zone %s: %ld\n",
|
||||
child->name,
|
||||
PTR_ERR(tz));
|
||||
continue;
|
||||
}
|
||||
|
||||
ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
|
||||
if (!ops)
|
||||
goto exit_free;
|
||||
|
||||
tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
|
||||
if (!tzp) {
|
||||
kfree(ops);
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
/* No hwmon because there might be hwmon drivers registering */
|
||||
tzp->no_hwmon = true;
|
||||
|
||||
zone = thermal_zone_device_register(child->name, tz->ntrips,
|
||||
0, tz,
|
||||
ops, tzp,
|
||||
tz->passive_delay,
|
||||
tz->polling_delay);
|
||||
if (IS_ERR(zone)) {
|
||||
pr_err("Failed to build %s zone %ld\n", child->name,
|
||||
PTR_ERR(zone));
|
||||
kfree(tzp);
|
||||
kfree(ops);
|
||||
of_thermal_free_zone(tz);
|
||||
/* attempting to build remaining zones still */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free:
|
||||
of_thermal_free_zone(tz);
|
||||
|
||||
/* no memory available, so free what we have built */
|
||||
of_thermal_destroy_zones();
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_thermal_destroy_zones - remove all zones parsed and allocated resources
|
||||
*
|
||||
* Finds all zones parsed and added to the thermal framework and remove them
|
||||
* from the system, together with their resources.
|
||||
*
|
||||
*/
|
||||
void of_thermal_destroy_zones(void)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np) {
|
||||
pr_err("unable to find thermal zones\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct thermal_zone_device *zone;
|
||||
|
||||
zone = thermal_zone_get_zone_by_name(child->name);
|
||||
if (IS_ERR(zone))
|
||||
continue;
|
||||
|
||||
thermal_zone_device_unregister(zone);
|
||||
kfree(zone->tzp);
|
||||
kfree(zone->ops);
|
||||
of_thermal_free_zone(zone->devdata);
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ static int exynos_get_trend(struct thermal_zone_device *thermal,
|
|||
return 0;
|
||||
}
|
||||
/* Operation callback functions for thermal zone */
|
||||
static struct thermal_zone_device_ops const exynos_dev_ops = {
|
||||
static struct thermal_zone_device_ops exynos_dev_ops = {
|
||||
.bind = exynos_bind,
|
||||
.unbind = exynos_unbind,
|
||||
.get_temp = exynos_get_temp,
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
|
|||
skip_calib_data:
|
||||
if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
|
||||
dev_err(&pdev->dev, "Invalid max trigger level\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/thermal.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
|
|
@ -403,7 +404,7 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
|
|||
enum thermal_trip_type type;
|
||||
#endif
|
||||
|
||||
if (!tz || IS_ERR(tz))
|
||||
if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
|
||||
goto exit;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
|
@ -459,6 +460,9 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
|
|||
{
|
||||
int count;
|
||||
|
||||
if (!tz->ops->get_temp)
|
||||
return;
|
||||
|
||||
update_temperature(tz);
|
||||
|
||||
for (count = 0; count < tz->trips; count++)
|
||||
|
|
@ -1055,7 +1059,8 @@ static struct class thermal_class = {
|
|||
};
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* __thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
|
|
@ -1063,13 +1068,16 @@ static struct class thermal_class = {
|
|||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
* It also gives the opportunity to link the cooling device to a device tree
|
||||
* node, so that it can be bound to a thermal zone created out of device tree.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_cooling_device_register(char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
static struct thermal_cooling_device *
|
||||
__thermal_cooling_device_register(struct device_node *np,
|
||||
char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
int result;
|
||||
|
|
@ -1094,6 +1102,7 @@ thermal_cooling_device_register(char *type, void *devdata,
|
|||
strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
|
||||
mutex_init(&cdev->lock);
|
||||
INIT_LIST_HEAD(&cdev->thermal_instances);
|
||||
cdev->np = np;
|
||||
cdev->ops = ops;
|
||||
cdev->updated = true;
|
||||
cdev->device.class = &thermal_class;
|
||||
|
|
@ -1136,8 +1145,52 @@ unregister:
|
|||
device_unregister(&cdev->device);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_cooling_device_register(char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
return __thermal_cooling_device_register(NULL, type, devdata, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_register);
|
||||
|
||||
/**
|
||||
* thermal_of_cooling_device_register() - register an OF thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This function will register a cooling device with device tree node reference.
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_of_cooling_device_register(struct device_node *np,
|
||||
char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
return __thermal_cooling_device_register(np, type, devdata, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_unregister - removes the registered thermal cooling device
|
||||
* @cdev: the thermal cooling device to remove.
|
||||
|
|
@ -1376,7 +1429,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
|
|||
*/
|
||||
struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
||||
int trips, int mask, void *devdata,
|
||||
const struct thermal_zone_device_ops *ops,
|
||||
struct thermal_zone_device_ops *ops,
|
||||
const struct thermal_zone_params *tzp,
|
||||
int passive_delay, int polling_delay)
|
||||
{
|
||||
|
|
@ -1392,7 +1445,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|||
if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!ops || !ops->get_temp)
|
||||
if (!ops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
|
||||
|
|
@ -1496,6 +1549,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|||
|
||||
INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
|
||||
|
||||
if (!tz->ops->get_temp)
|
||||
thermal_zone_device_set_polling(tz, 0);
|
||||
|
||||
thermal_zone_device_update(tz);
|
||||
|
||||
if (!result)
|
||||
|
|
@ -1746,8 +1802,14 @@ static int __init thermal_init(void)
|
|||
if (result)
|
||||
goto unregister_class;
|
||||
|
||||
result = of_parse_thermal_zones();
|
||||
if (result)
|
||||
goto exit_netlink;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_netlink:
|
||||
genetlink_exit();
|
||||
unregister_governors:
|
||||
thermal_unregister_governors();
|
||||
unregister_class:
|
||||
|
|
@ -1763,6 +1825,7 @@ error:
|
|||
|
||||
static void __exit thermal_exit(void)
|
||||
{
|
||||
of_thermal_destroy_zones();
|
||||
genetlink_exit();
|
||||
class_unregister(&thermal_class);
|
||||
thermal_unregister_governors();
|
||||
|
|
|
|||
|
|
@ -77,4 +77,13 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
|
|||
static inline void thermal_gov_user_space_unregister(void) {}
|
||||
#endif /* CONFIG_THERMAL_GOV_USER_SPACE */
|
||||
|
||||
/* device tree support */
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
int of_parse_thermal_zones(void);
|
||||
void of_thermal_destroy_zones(void);
|
||||
#else
|
||||
static inline int of_parse_thermal_zones(void) { return 0; }
|
||||
static inline void of_thermal_destroy_zones(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* __THERMAL_CORE_H__ */
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "ti-thermal.h"
|
||||
#include "ti-bandgap.h"
|
||||
|
|
@ -44,6 +45,7 @@ struct ti_thermal_data {
|
|||
enum thermal_device_mode mode;
|
||||
struct work_struct thermal_wq;
|
||||
int sensor_id;
|
||||
bool our_zone;
|
||||
};
|
||||
|
||||
static void ti_thermal_work(struct work_struct *work)
|
||||
|
|
@ -75,11 +77,10 @@ static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
|
|||
|
||||
/* thermal zone ops */
|
||||
/* Get temperature callback function for thermal zone*/
|
||||
static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
unsigned long *temp)
|
||||
static inline int __ti_thermal_get_temp(void *devdata, long *temp)
|
||||
{
|
||||
struct thermal_zone_device *pcb_tz = NULL;
|
||||
struct ti_thermal_data *data = thermal->devdata;
|
||||
struct ti_thermal_data *data = devdata;
|
||||
struct ti_bandgap *bgp;
|
||||
const struct ti_temp_sensor *s;
|
||||
int ret, tmp, slope, constant;
|
||||
|
|
@ -118,6 +119,14 @@ static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct ti_thermal_data *data = thermal->devdata;
|
||||
|
||||
return __ti_thermal_get_temp(data, temp);
|
||||
}
|
||||
|
||||
/* Bind callback functions for thermal zone */
|
||||
static int ti_thermal_bind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
|
|
@ -230,11 +239,9 @@ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get the temperature trend callback functions for thermal zone */
|
||||
static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trend *trend)
|
||||
static int __ti_thermal_get_trend(void *p, long *trend)
|
||||
{
|
||||
struct ti_thermal_data *data = thermal->devdata;
|
||||
struct ti_thermal_data *data = p;
|
||||
struct ti_bandgap *bgp;
|
||||
int id, tr, ret = 0;
|
||||
|
||||
|
|
@ -245,6 +252,22 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*trend = tr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the temperature trend callback functions for thermal zone */
|
||||
static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trend *trend)
|
||||
{
|
||||
int ret;
|
||||
long tr;
|
||||
|
||||
ret = __ti_thermal_get_trend(thermal->devdata, &tr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tr > 0)
|
||||
*trend = THERMAL_TREND_RAISING;
|
||||
else if (tr < 0)
|
||||
|
|
@ -308,16 +331,23 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
|
|||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
/* Create thermal zone */
|
||||
data->ti_thermal = thermal_zone_device_register(domain,
|
||||
/* in case this is specified by DT */
|
||||
data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id,
|
||||
data, __ti_thermal_get_temp,
|
||||
__ti_thermal_get_trend);
|
||||
if (IS_ERR(data->ti_thermal)) {
|
||||
/* Create thermal zone */
|
||||
data->ti_thermal = thermal_zone_device_register(domain,
|
||||
OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops,
|
||||
NULL, FAST_TEMP_MONITORING_RATE,
|
||||
FAST_TEMP_MONITORING_RATE);
|
||||
if (IS_ERR(data->ti_thermal)) {
|
||||
dev_err(bgp->dev, "thermal zone device is NULL\n");
|
||||
return PTR_ERR(data->ti_thermal);
|
||||
if (IS_ERR(data->ti_thermal)) {
|
||||
dev_err(bgp->dev, "thermal zone device is NULL\n");
|
||||
return PTR_ERR(data->ti_thermal);
|
||||
}
|
||||
data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
|
||||
data->our_zone = true;
|
||||
}
|
||||
data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
|
||||
ti_bandgap_set_sensor_data(bgp, id, data);
|
||||
ti_bandgap_write_update_interval(bgp, data->sensor_id,
|
||||
data->ti_thermal->polling_delay);
|
||||
|
|
@ -331,7 +361,13 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
|
|||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
|
||||
thermal_zone_device_unregister(data->ti_thermal);
|
||||
if (data && data->ti_thermal) {
|
||||
if (data->our_zone)
|
||||
thermal_zone_device_unregister(data->ti_thermal);
|
||||
else
|
||||
thermal_zone_of_sensor_unregister(bgp->dev,
|
||||
data->ti_thermal);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -350,6 +386,15 @@ int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
|
|||
int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
|
||||
{
|
||||
struct ti_thermal_data *data;
|
||||
struct device_node *np = bgp->dev->of_node;
|
||||
|
||||
/*
|
||||
* We are assuming here that if one deploys the zone
|
||||
* using DT, then it must be aware that the cooling device
|
||||
* loading has to happen via cpufreq driver.
|
||||
*/
|
||||
if (of_find_property(np, "#thermal-sensor-cells", NULL))
|
||||
return 0;
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
if (!data || IS_ERR(data))
|
||||
|
|
@ -380,7 +425,9 @@ int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
|
|||
struct ti_thermal_data *data;
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
cpufreq_cooling_unregister(data->cool_dev);
|
||||
|
||||
if (data && data->cool_dev)
|
||||
cpufreq_cooling_unregister(data->cool_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
|
||||
static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
u32 l, h;
|
||||
|
|
|
|||
Loading…
Reference in a new issue