Commit 34183ddd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'thermal-v5.7-rc1' of git://

Pull thermal updates from Daniel Lezcano:

 - Convert tsens configuration DT binding to yaml (Rajeshwari)

 - Add interrupt support on the rcar sensor (Niklas Söderlund)

 - Add a new Spreadtrum thermal driver (Baolin Wang)

 - Add thermal binding for the fsl scu board, a new API to retrieve the
   sensor id bound to the thermal zone and i.MX system controller sensor
   (Anson Huang))

 - Remove warning log when a deferred probe is requested on Exynos
   (Marek Szyprowski)

 - Add the thermal monitoring unit support for imx8mm with its DT
   bindings (Anson Huang)

 - Rephrase the Kconfig text for clarity (Linus Walleij)

 - Use the gpio descriptor for the ti-soc-thermal (Linus Walleij)

 - Align msg structure to 4 bytes for i.MX SC, fix the Kconfig
   dependency, add the __may_be unused annotation for PM functions and
   the COMPILE_TEST option for imx8mm (Anson Huang)

 - Fix a dependency on regmap in Kconfig for qoriq (Yuantian Tang)

 - Add DT binding and support for the rcar gen3 r8a77961 and improve the
   error path on the rcar init function (Niklas Söderlund)

 - Cleanup and improvements for the tsens Qcom sensor (Amit Kucheria)

 - Improve code by removing lock and caching values in the rcar thermal
   sensor (Niklas Söderlund)

 - Cleanup in the qoriq drivers and add a call to
   imx_thermal_unregister_legacy_cooling in the removal function (Anson

 - Remove redundant 'maxItems' in tsens and sprd DT bindings (Rob

 - Change the thermal DT bindings by making the cooling-maps optional
   (Yuantian Tang)

 - Add Tiger Lake support (Sumeet Pawnikar)

 - Use scnprintf() for avoiding potential buffer overflow (Takashi Iwai)

 - Make pkg_temp_lock a raw_spinlock_t(Clark Williams)

 - Fix incorrect data types by changing them to signed on i.MX SC (Anson

 - Replace zero-length array with flexible-array member (Gustavo A. R.

 - Add support for i.MX8MP in the driver and in the DT bindings (Anson

 - Fix return value of the cpufreq_set_cur_state() function (Willy

 - Remove abusing and scary WARN_ON in the cpufreq cooling device
   (Daniel Lezcano)

 - Fix build warning of incorrect argument type reported by sparse on
   imx8mm (Anson Huang)

 - Fix stub for the devfreq cooling device (Martin Blumenstingl)

 - Fix cpu idle cooling documentation (Sergey Vidishev)

* tag 'thermal-v5.7-rc1' of git:// (52 commits)
  Documentation: cpu-idle-cooling: Fix diagram for 33% duty cycle
  thermal: devfreq_cooling: inline all stubs for CONFIG_DEVFREQ_THERMAL=n
  thermal: imx8mm: Fix build warning of incorrect argument type
  thermal/drivers/cpufreq_cooling: Remove abusing WARN_ON
  thermal/drivers/cpufreq_cooling: Fix return of cpufreq_set_cur_state
  thermal: imx8mm: Add i.MX8MP support
  dt-bindings: thermal: imx8mm-thermal: Add support for i.MX8MP
  thermal: qcom: tsens.h: Replace zero-length array with flexible-array member
  thermal: imx_sc_thermal: Fix incorrect data type
  thermal: int340x_thermal: Use scnprintf() for avoiding potential buffer overflow
  thermal: int340x: processor_thermal: Add Tiger Lake support
  thermal/x86_pkg_temp: Make pkg_temp_lock a raw_spinlock_t
  dt-bindings: thermal: make cooling-maps property optional
  dt-bindings: thermal: qcom-tsens: Remove redundant 'maxItems'
  dt-bindings: thermal: sprd: Remove redundant 'maxItems'
  thermal: imx: Calling imx_thermal_unregister_legacy_cooling() in .remove
  thermal: qoriq: Sort includes alphabetically
  thermal: qoriq: Use devm_add_action_or_reset() to handle all cleanups
  thermal: rcar_thermal: Remove lock in rcar_thermal_get_current_temp()
  thermal: rcar_thermal: Do not store ctemp in rcar_thermal_priv
parents 8645f09b 11700fcb
......@@ -166,6 +166,17 @@ Required properties:
followed by "fsl,imx-sc-key";
- linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
Thermal bindings based on SCU Message Protocol
Required properties:
- compatible: Should be :
followed by "fsl,imx-sc-thermal";
- #thermal-sensor-cells: See Documentation/devicetree/bindings/thermal/thermal.txt
for a description.
Example (imx8qxp):
aliases {
......@@ -238,6 +249,11 @@ firmware {
compatible = "fsl,imx8qxp-sc-wdt", "fsl,imx-sc-wdt";
timeout-sec = <60>;
tsens: thermal-sensor {
compatible = "fsl,imx8qxp-sc-thermal", "fsl,imx-sc-thermal";
#thermal-sensor-cells = <1>;
* Thermal Monitoring Unit (TMU) on Freescale i.MX8MM SoC
Required properties:
- compatible : Must be "fsl,imx8mm-tmu" or "fsl,imx8mp-tmu".
- reg : Address range of TMU registers.
- clocks : TMU's clock source.
- #thermal-sensor-cells : Should be 0 or 1. See ./thermal.txt for a description.
tmu: tmu@30260000 {
compatible = "fsl,imx8mm-tmu";
reg = <0x30260000 0x10000>;
clocks = <&clk IMX8MM_CLK_TMU_ROOT>;
#thermal-sensor-cells = <0>;
......@@ -38,11 +38,11 @@ properties:
- enum:
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,sc7180-tsens
- qcom,sdm845-tsens
- const: qcom,tsens-v2
maxItems: 2
- description: TM registers
- description: SROT registers
......@@ -11,6 +11,7 @@ Required properties:
- "renesas,r8a774b1-thermal" (RZ/G2N)
- "renesas,r8a7795-thermal" (R-Car H3)
- "renesas,r8a7796-thermal" (R-Car M3-W)
- "renesas,r8a77961-thermal" (R-Car M3-W+)
- "renesas,r8a77965-thermal" (R-Car M3-N)
- "renesas,r8a77980-thermal" (R-Car V3H)
- reg : Address ranges of the thermal registers. Each sensor
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
title: Spreadtrum thermal sensor controller bindings
- Orson Zhai <>
- Baolin Wang <>
- Chunyan Zhang <>
const: sprd,ums512-thermal
maxItems: 1
maxItems: 1
- const: enable
maxItems: 2
Reference to nvmem nodes for the calibration data.
- const: thm_sign_cal
- const: thm_ratio_cal
const: 1
const: 1
const: 0
type: object
Represent one thermal sensor.
description: Specify the sensor id.
maxItems: 1
maxItems: 1
Reference to an nvmem node for the calibration data.
const: sen_delta_cal
- reg
- nvmem-cells
- nvmem-cell-names
- compatible
- reg
- clocks
- clock-names
- nvmem-cells
- nvmem-cell-names
- "#thermal-sensor-cells"
- "#address-cells"
- "#size-cells"
- |
ap_thm0: thermal@32200000 {
compatible = "sprd,ums512-thermal";
reg = <0 0x32200000 0 0x10000>;
clock-names = "enable";
clocks = <&aonapb_gate 32>;
#thermal-sensor-cells = <1>;
nvmem-cells = <&thm0_sign>, <&thm0_ratio>;
nvmem-cell-names = "thm_sign_cal", "thm_ratio_cal";
#address-cells = <1>;
#size-cells = <0>;
prometheus-sensor@0 {
reg = <0>;
nvmem-cells = <&thm0_sen0>;
nvmem-cell-names = "sen_delta_cal";
ank-sensor@1 {
reg = <1>;
nvmem-cells = <&thm0_sen1>;
nvmem-cell-names = "sen_delta_cal";
......@@ -142,11 +142,11 @@ Required properties:
- trips: A sub-node which is a container of only trip point nodes
Type: sub-node required to describe the thermal zone.
Optional property:
- cooling-maps: A sub-node which is a container of only cooling device
Type: sub-node map nodes, used to describe the relation between trips
and cooling devices.
Optional property:
- coefficients: An array of integers (one signed cell) containing
Type: array coefficients to compose a linear relation between
Elem size: one cell the sensors listed in the thermal-sensors property.
......@@ -105,8 +105,8 @@ and this variation will modulate the cooling effect.
idle <-------------->
duty cycle 33%
duty cycle 33%
# SPDX-License-Identifier: GPL-2.0-only
# Generic thermal sysfs drivers configuration
# Generic thermal drivers configuration
menuconfig THERMAL
bool "Generic Thermal sysfs driver"
bool "Thermal drivers"
Generic Thermal Sysfs driver offers a generic mechanism for
Thermal drivers offer a generic mechanism for
thermal management. Usually it's made up of one or more thermal
zone and cooling device.
zones and cooling devices.
Each thermal zone contains its own temperature, trip points,
cooling devices.
All platforms with ACPI thermal support can use this driver.
and cooling devices.
All platforms with ACPI or Open Firmware thermal support can use
this driver.
If you want this support, you should say Y here.
......@@ -251,6 +252,27 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
tristate "Temperature sensor driver for NXP i.MX SoCs with System Controller"
depends on IMX_SCU
depends on OF
Support for Temperature Monitor (TEMPMON) found on NXP i.MX SoCs with
system controller inside, Linux kernel has to communicate with system
controller via MU (message unit) IPC to get temperature from thermal
sensor. It supports one critical trip point and one
passive trip point for each thermal sensor.
tristate "Temperature sensor driver for Freescale i.MX8MM SoC"
depends on OF
Support for Thermal Monitoring Unit (TMU) found on Freescale i.MX8MM SoC.
It supports one critical trip point and one passive trip point. The
cpufreq is used as the cooling device to throttle CPUs when the passive
trip is crossed.
config MAX77620_THERMAL
tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
depends on MFD_MAX77620
......@@ -265,6 +287,7 @@ config QORIQ_THERMAL
tristate "QorIQ Thermal Monitoring Unit"
depends on THERMAL_OF
depends on HAS_IOMEM
Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
It supports one critical trip point and one passive trip point. The
......@@ -460,4 +483,11 @@ config UNIPHIER_THERMAL
Enable this to plug in UniPhier on-chip PVT thermal driver into the
thermal framework. The driver supports CPU thermal zone temperature
reporting and a couple of trip points.
tristate "Temperature sensor on Spreadtrum SoCs"
Support for the Spreadtrum thermal sensor driver in the Linux thermal
......@@ -43,6 +43,8 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
......@@ -57,3 +59,4 @@ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
......@@ -273,7 +273,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev,
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
/* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_cdev->max_level))
if (state > cpufreq_cdev->max_level)
return -EINVAL;
num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
......@@ -437,7 +437,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
int ret;
/* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_cdev->max_level))
if (state > cpufreq_cdev->max_level)
return -EINVAL;
/* Check if the old cooling action is same as new cooling action */
......@@ -456,6 +456,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
capacity = frequency * max_capacity;
capacity /= cpufreq_cdev->policy->cpuinfo.max_freq;
arch_set_thermal_pressure(cpus, max_capacity - capacity);
ret = 0;
return ret;
// SPDX-License-Identifier: GPL-2.0
* Copyright 2020 NXP.
* Author: Anson Huang <>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include "thermal_core.h"
#define TER 0x0 /* TMU enable */
#define TPS 0x4
#define TRITSR 0x20 /* TMU immediate temp */
#define TER_EN BIT(31)
#define TRITSR_TEMP0_VAL_MASK 0xff
#define TRITSR_TEMP1_VAL_MASK 0xff0000
#define PROBE_SEL_ALL GENMASK(31, 30)
#define probe_status_offset(x) (30 + x)
#define SIGN_BIT BIT(7)
#define VER1_TEMP_LOW_LIMIT 10000
#define VER2_TEMP_LOW_LIMIT -40000
#define VER2_TEMP_HIGH_LIMIT 125000
#define TMU_VER1 0x1
#define TMU_VER2 0x2
struct thermal_soc_data {
u32 num_sensors;
u32 version;
int (*get_temp)(void *, int *);
struct tmu_sensor {
struct imx8mm_tmu *priv;
u32 hw_id;
struct thermal_zone_device *tzd;
struct imx8mm_tmu {
void __iomem *base;
struct clk *clk;
const struct thermal_soc_data *socdata;
struct tmu_sensor sensors[0];
static int imx8mm_tmu_get_temp(void *data, int *temp)
struct tmu_sensor *sensor = data;
struct imx8mm_tmu *tmu = sensor->priv;
u32 val;
val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK;
*temp = val * 1000;
if (*temp < VER1_TEMP_LOW_LIMIT)
return -EAGAIN;
return 0;
static int imx8mp_tmu_get_temp(void *data, int *temp)
struct tmu_sensor *sensor = data;
struct imx8mm_tmu *tmu = sensor->priv;
unsigned long val;
bool ready;
val = readl_relaxed(tmu->base + TRITSR);
ready = test_bit(probe_status_offset(sensor->hw_id), &val);
if (!ready)
return -EAGAIN;
val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) :
if (val & SIGN_BIT) /* negative */
val = (~(val & TEMP_VAL_MASK) + 1);
*temp = val * 1000;
if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
return -EAGAIN;
return 0;
static int tmu_get_temp(void *data, int *temp)
struct tmu_sensor *sensor = data;
struct imx8mm_tmu *tmu = sensor->priv;
return tmu->socdata->get_temp(data, temp);
static struct thermal_zone_of_device_ops tmu_tz_ops = {
.get_temp = tmu_get_temp,
static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable)
u32 val;
val = readl_relaxed(tmu->base + TER);
val = enable ? (val | TER_EN) : (val & ~TER_EN);
writel_relaxed(val, tmu->base + TER);
static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu)
u32 val;
val = readl_relaxed(tmu->base + TPS);
writel_relaxed(val, tmu->base + TPS);
static int imx8mm_tmu_probe(struct platform_device *pdev)
const struct thermal_soc_data *data;
struct imx8mm_tmu *tmu;
int ret;
int i;
data = of_device_get_match_data(&pdev->dev);
tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors,
data->num_sensors), GFP_KERNEL);
if (!tmu)
return -ENOMEM;
tmu->socdata = data;
tmu->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tmu->base))
return PTR_ERR(tmu->base);
tmu->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tmu->clk)) {
ret = PTR_ERR(tmu->clk);
if (ret != -EPROBE_DEFER)
"failed to get tmu clock: %d\n", ret);
return ret;
ret = clk_prepare_enable(tmu->clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
return ret;
/* disable the monitor during initialization */
imx8mm_tmu_enable(tmu, false);
for (i = 0; i < data->num_sensors; i++) {
tmu->sensors[i].priv = tmu;
tmu->sensors[i].tzd =
devm_thermal_zone_of_sensor_register(&pdev->dev, i,
if (IS_ERR(tmu->sensors[i].tzd)) {
"failed to register thermal zone sensor[%d]: %d\n",
i, ret);
return PTR_ERR(tmu->sensors[i].tzd);
tmu->sensors[i].hw_id = i;
platform_set_drvdata(pdev, tmu);
/* enable all the probes for V2 TMU */
if (tmu->socdata->version == TMU_VER2)
/* enable the monitor */
imx8mm_tmu_enable(tmu, true);
return 0;
static int imx8mm_tmu_remove(struct platform_device *pdev)
struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
/* disable TMU */
imx8mm_tmu_enable(tmu, false);
platform_set_drvdata(pdev, NULL);
return 0;
static struct thermal_soc_data imx8mm_tmu_data = {
.num_sensors = 1,
.version = TMU_VER1,
.get_temp = imx8mm_tmu_get_temp,
static struct thermal_soc_data imx8mp_tmu_data = {
.num_sensors = 2,
.version = TMU_VER2,
.get_temp = imx8mp_tmu_get_temp,
static const struct of_device_id imx8mm_tmu_table[] = {
{ .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, },
{ .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, },
{ },
static struct platform_driver imx8mm_tmu = {
.driver = {
.name = "i.mx8mm_thermal",
.of_match_table = imx8mm_tmu_table,
.probe = imx8mm_tmu_probe,
.remove = imx8mm_tmu_remove,
MODULE_AUTHOR("Anson Huang <>");
MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
// SPDX-License-Identifier: GPL-2.0+
* Copyright 2018-2020 NXP.
#include <linux/err.h>
#include <linux/firmware/imx/sci.h>
#include <linux/firmware/imx/types.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "thermal_core.h"
static struct imx_sc_ipc *thermal_ipc_handle;
struct imx_sc_sensor {
struct thermal_zone_device *tzd;
u32 resource_id;
struct req_get_temp {
u16 resource_id;
u8 type;
} __packed __aligned(4);
struct resp_get_temp {
s16 celsius;
s8 tenths;
} __packed __aligned(4);
struct imx_sc_msg_misc_get_temp {
struct imx_sc_rpc_msg hdr;
union {
struct req_get_temp req;
struct resp_get_temp resp;
} data;
} __packed __aligned(4);
static int imx_sc_thermal_get_temp(void *data, int *temp)
struct imx_sc_msg_misc_get_temp msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
struct imx_sc_sensor *sensor = data;
int ret; = sensor->resource_id; = IMX_SC_C_TEMP;
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = IMX_SC_RPC_SVC_MISC;
hdr->size = 2;
ret = imx_scu_call_rpc(thermal_ipc_handle, &msg, true);
if (ret) {
dev_err(&sensor->tzd->device, "read temp sensor %d failed, ret %d\n",
sensor->resource_id, ret);
return ret;
*temp = * 1000 + * 100;
return 0;
static const struct thermal_zone_of_device_ops imx_sc_thermal_ops = {
.get_temp = imx_sc_thermal_get_temp,
static int imx_sc_thermal_probe(struct platform_device *pdev)
struct device_node *np, *child, *sensor_np;
struct imx_sc_sensor *sensor;
int ret;
ret = imx_scu_get_handle(&thermal_ipc_handle);
if (ret)
return ret;
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np)
return -ENODEV;
sensor_np = of_node_get(pdev->dev.of_node);
for_each_available_child_of_node(np, child) {
sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor) {
return -ENOMEM;
ret = thermal_zone_of_get_sensor_id(child,
if (ret < 0) {
"failed to get valid sensor resource id: %d\n",
sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
if (IS_ERR(sensor->tzd)) {
dev_err(&pdev->dev, "failed to register thermal zone\n");
ret = PTR_ERR(sensor->tzd);
return ret;
static int imx_sc_thermal_remove(struct platform_device *pdev)
return 0;