From 4e5e4705bf69ea450f58fc709ac5888f321a9299 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 3 Jul 2013 15:35:39 -0400 Subject: thermal: introduce device tree parser This patch introduces a device tree bindings for describing the hardware thermal behavior and limits. Also a parser to read and interpret the data and feed it in the thermal framework is presented. This patch introduces a thermal data parser for device tree. The parsed data is used to build thermal zones and thermal binding parameters. The output data can then be used to deploy thermal policies. This patch adds also documentation regarding this API and how to define tree nodes to use this infrastructure. Note that, in order to be able to have control on the sensor registration on the DT thermal zone, it was required to allow changing the thermal zone .get_temp callback. For this reason, this patch also removes the 'const' modifier from the .ops field of thermal zone devices. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Acked-by: Mark Rutland Signed-off-by: Eduardo Valentin --- drivers/thermal/Kconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/thermal/Kconfig') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index f35a1f75b15b..a150f8d53322 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -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 -- cgit v1.2.3 From 39d99cff76bf2992fd6dd4b1fc62da139e62e90c Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Thu, 12 Sep 2013 19:26:45 -0400 Subject: thermal: cpu_cooling: introduce of_cpufreq_cooling_register This patch introduces an API to register cpufreq cooling device based on device tree node. The registration via device tree node differs from normal registration due to the fact that it is needed to fill the device_node structure in order to be able to match the cooling devices with trip points. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin --- drivers/thermal/Kconfig | 1 + drivers/thermal/cpu_cooling.c | 56 ++++++++++++++++++++++++++++++++++++++----- include/linux/cpu_cooling.h | 25 +++++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) (limited to 'drivers/thermal/Kconfig') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index a150f8d53322..3feb5377fbf6 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -92,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 diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 02a46f23d14c..a6cb5531403f 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -417,18 +417,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; @@ -467,8 +470,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); @@ -488,8 +491,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. diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index a5d52eea8232..c303d383def1 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -24,6 +24,7 @@ #ifndef __CPU_COOLING_H__ #define __CPU_COOLING_H__ +#include #include #include @@ -35,6 +36,24 @@ struct thermal_cooling_device * cpufreq_cooling_register(const struct cpumask *clip_cpus); +/** + * of_cpufreq_cooling_register - create cpufreq cooling device based on DT. + * @np: a valid struct device_node to the cooling device device tree node. + * @clip_cpus: cpumask of cpus where the frequency constraints will happen + */ +#ifdef CONFIG_THERMAL_OF +struct thermal_cooling_device * +of_cpufreq_cooling_register(struct device_node *np, + const struct cpumask *clip_cpus); +#else +static inline struct thermal_cooling_device * +of_cpufreq_cooling_register(struct device_node *np, + const struct cpumask *clip_cpus) +{ + return NULL; +} +#endif + /** * cpufreq_cooling_unregister - function to remove cpufreq cooling device. * @cdev: thermal cooling device pointer. @@ -48,6 +67,12 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus) { return NULL; } +static inline struct thermal_cooling_device * +of_cpufreq_cooling_register(struct device_node *np, + const struct cpumask *clip_cpus) +{ + return NULL; +} static inline void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) { -- cgit v1.2.3 From 925c36bb09bdf3858c10d5a591be737dd133d979 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 30 Dec 2013 12:55:06 -0800 Subject: Thermal: ACPI INT3403 thermal driver The ACPI INT3403 device objects present on some systems can be used to retrieve temperature data from thermal sensors. Add a driver registering each INT3403 device object as a thermal zone device and exposing its _TMP, PATx and GTSH method via the standard thermal control interface under /sys/class/thermal/. Signed-off-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 7 ++ drivers/thermal/Makefile | 1 + drivers/thermal/int3403_thermal.c | 237 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 drivers/thermal/int3403_thermal.c (limited to 'drivers/thermal/Kconfig') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index f35a1f75b15b..8928e6447380 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -192,6 +192,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 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 584b36319d51..aa1bba92784e 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -29,3 +29,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 diff --git a/drivers/thermal/int3403_thermal.c b/drivers/thermal/int3403_thermal.c new file mode 100644 index 000000000000..1301681d9a77 --- /dev/null +++ b/drivers/thermal/int3403_thermal.c @@ -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 +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ACPI INT3403 thermal driver"); -- cgit v1.2.3 From beeb5a1e0ef774d0a3e18c5a0e78c39e3e1a5389 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 27 Nov 2013 02:18:34 +0100 Subject: thermal: rcar-thermal: Enable driver compilation with COMPILE_TEST This helps increasing build testing coverage. Cc: Zhang Rui Cc: Eduardo Valentin Cc: linux-pm@vger.kernel.org Signed-off-by: Laurent Pinchart Acked-by: Simon Horman Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal/Kconfig') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index f35a1f75b15b..661cafc756ac 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -121,7 +121,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. -- cgit v1.2.3