Thermal control updates for 6.19-rc1

- Add Nova Lake processor thermal device to the int340x
    processor_thermal driver, add DLVR support for Nova Lake to it,
    add Nova Lake support to the ACPI DPTF code, document thermal
    throttling on Intel platforms, and update workload type hint
    interface documentation (Srinivas Pandruvada)
 
  - Remove int340x thermal scan handler from the ACPI DPTF code
    because it turned out to be unnecessary (Slawomir Rosek)
 
  - Clean up the Intel int340x thermal driver (Kaushlendra Kumar)
 
  - Document the RZ/V2H TSU DT bindings (Ovidiu Panait)
 
  - Document the Kaanapali Temperature Sensor (Manaf Meethalavalappu
    Pallikunhi)
 
  - Document R-Car Gen4 and RZ/G2 support in driver comment (Marek Vasut)
 
  - Convert to DEFINE_SIMPLE_DEV_PM_OPS() in R-Car [Gen3] (Geert
    Uytterhoeven)
 
  - Fix format string bug in thermal-engine (Malaya Kumar Rout)
 
  - Make ipq5018 tsens standalone compatible (George Moussalem)
 
  - Add the QCS8300 compatible for QCom Tsens (Gaurav Kohli)
 
  - Add support for the NXP i.MX91 thermal module, including the DT
    bindings (Pengfei Li)
 -----BEGIN PGP SIGNATURE-----
 
 iQFGBAABCAAwFiEEcM8Aw/RY0dgsiRUR7l+9nS/U47UFAmkpt1cSHHJqd0Byand5
 c29ja2kubmV0AAoJEO5fvZ0v1OO1cikH/jW6IUXUvrTy9VEi3wGTzLAcnOuJJtQL
 zKQBzrjtuGngr4xIeE+chr9Gr8+S4EfVcD17twp59I6C3T9fBZngfMxbi7VLdyd7
 gIJs2IxIqfIlQwK32lBOkLM2/YHa0AYU3Dd/YHsgYOU3Y25adSvmiwoTqG3kUmXB
 YnoHUobPskzV/9iKf2sptM7XBLDaBdoPHcDAM2BN4rfKhgOy/1ha7KzigJyuHHyW
 V+b9KR/IlFOVct8OrvhXKT4mzsS9VZv6IJ6KrRsDsCIgVwM/fO1YTmRgfOkuRASZ
 fw6vNNi+49xK0LN8zEvHKuYthBFy+7lIP+MiErMr2Fw5nuY2uXitZA8=
 =OgXe
 -----END PGP SIGNATURE-----

Merge tag 'thermal-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull thermal control updates from Rafael Wysocki:
 "These add Nova Lake processor support to the Intel thermal drivers and
  DPTF code, update thermal control documentation, simplify the ACPI
  DPTF code related to thermal control, add QCS8300 compatible to the
  tsens thermal DT bindings, add DT bindings for NXP i.MX91 thermal
  module and add support for it to the imx91 thermal driver, update a
  few other thermal drivers and fix a format string issue in a thermal
  utility:

   - Add Nova Lake processor thermal device to the int340x
     processor_thermal driver, add DLVR support for Nova Lake to it, add
     Nova Lake support to the ACPI DPTF code, document thermal
     throttling on Intel platforms, and update workload type hint
     interface documentation (Srinivas Pandruvada)

   - Remove int340x thermal scan handler from the ACPI DPTF code because
     it turned out to be unnecessary (Slawomir Rosek)

   - Clean up the Intel int340x thermal driver (Kaushlendra Kumar)

   - Document the RZ/V2H TSU DT bindings (Ovidiu Panait)

   - Document the Kaanapali Temperature Sensor (Manaf Meethalavalappu
     Pallikunhi)

   - Document R-Car Gen4 and RZ/G2 support in driver comment (Marek
     Vasut)

   - Convert to DEFINE_SIMPLE_DEV_PM_OPS() in R-Car [Gen3] (Geert
     Uytterhoeven)

   - Fix format string bug in thermal-engine (Malaya Kumar Rout)

   - Make ipq5018 tsens standalone compatible (George Moussalem)

   - Add the QCS8300 compatible for QCom Tsens (Gaurav Kohli)

   - Add support for the NXP i.MX91 thermal module, including the DT
     bindings (Pengfei Li)"

* tag 'thermal-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  thermal/drivers/imx91: Add support for i.MX91 thermal monitoring unit
  dt-bindings: thermal: fsl,imx91-tmu: add bindings for NXP i.MX91 thermal module
  dt-bindings: thermal: tsens: Add QCS8300 compatible
  dt-bindings: thermal: qcom-tsens: make ipq5018 tsens standalone compatible
  tools/thermal/thermal-engine: Fix format string bug in thermal-engine
  docs: driver-api/thermal/intel_dptf: Add new workload type hint
  thermal/drivers/rcar_gen3: Convert to DEFINE_SIMPLE_DEV_PM_OPS()
  thermal/drivers/rcar: Convert to DEFINE_SIMPLE_DEV_PM_OPS()
  Documentation: thermal: Document thermal throttling on Intel platforms
  ACPI: DPTF: Support Nova Lake
  thermal: intel: int340x: Add DLVR support for Nova Lake
  thermal: int340x: processor_thermal: Add Nova Lake processor thermal device
  thermal: intel: int340x: Replace sprintf() with sysfs_emit()
  thermal: intel: int340x: Use symbolic constant for UUID comparison
  thermal/drivers/rcar_gen3: Document R-Car Gen4 and RZ/G2 support in driver comment
  dt-bindings: thermal: qcom-tsens: document the Kaanapali Temperature Sensor
  dt-bindings: thermal: r9a09g047-tsu: Document RZ/V2H TSU
  ACPI: DPTF: Remove int340x thermal scan handler
  thermal: intel: Select INT340X_THERMAL from INTEL_SOC_DTS_THERMAL
This commit is contained in:
Linus Torvalds 2025-12-02 17:49:12 -08:00
commit 619f4edc8d
24 changed files with 660 additions and 116 deletions

View File

@ -6,3 +6,4 @@ Thermal Subsystem
:maxdepth: 1
intel_powerclamp
intel_thermal_throttle

View File

@ -0,0 +1,91 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
=======================================
Intel thermal throttle events reporting
=======================================
:Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Introduction
------------
Intel processors have built in automatic and adaptive thermal monitoring
mechanisms that force the processor to reduce its power consumption in order
to operate within predetermined temperature limits.
Refer to section "THERMAL MONITORING AND PROTECTION" in the "Intel® 64 and
IA-32 Architectures Software Developers Manual Volume 3 (3A, 3B, 3C, & 3D):
System Programming Guide" for more details.
In general, there are two mechanisms to control the core temperature of the
processor. They are called "Thermal Monitor 1 (TM1) and Thermal Monitor 2 (TM2)".
The status of the temperature sensor that triggers the thermal monitor (TM1/TM2)
is indicated through the "thermal status flag" and "thermal status log flag" in
MSR_IA32_THERM_STATUS for core level and MSR_IA32_PACKAGE_THERM_STATUS for
package level.
Thermal Status flag, bit 0 — When set, indicates that the processor core
temperature is currently at the trip temperature of the thermal monitor and that
the processor power consumption is being reduced via either TM1 or TM2, depending
on which is enabled. When clear, the flag indicates that the core temperature is
below the thermal monitor trip temperature. This flag is read only.
Thermal Status Log flag, bit 1 — When set, indicates that the thermal sensor has
tripped since the last power-up or reset or since the last time that software
cleared this flag. This flag is a sticky bit; once set it remains set until
cleared by software or until a power-up or reset of the processor. The default
state is clear.
It is possible that when user reads MSR_IA32_THERM_STATUS or
MSR_IA32_PACKAGE_THERM_STATUS, TM1/TM2 is not active. In this case,
"Thermal Status flag" will read "0" and the "Thermal Status Log flag" will be set
to show any previous "TM1/TM2" activation. But since it needs to be cleared by
the software, it can't show the number of occurrences of "TM1/TM2" activations.
Hence, Linux provides counters of how many times the "Thermal Status flag" was
set. Also presents how long the "Thermal Status flag" was active in milliseconds.
Using these counters, users can check if the performance was limited because of
thermal events. It is recommended to read from sysfs instead of directly reading
MSRs as the "Thermal Status Log flag" is reset by the driver to implement rate
control.
Sysfs Interface
---------------
Thermal throttling events are presented for each CPU under
"/sys/devices/system/cpu/cpuX/thermal_throttle/", where "X" is the CPU number.
All these counters are read-only. They can't be reset to 0. So, they can potentially
overflow after reaching the maximum 64 bit unsigned integer.
``core_throttle_count``
Shows the number of times "Thermal Status flag" changed from 0 to 1 for this
CPU since OS boot and thermal vector is initialized. This is a 64 bit counter.
``package_throttle_count``
Shows the number of times "Thermal Status flag" changed from 0 to 1 for the
package containing this CPU since OS boot and thermal vector is initialized.
Package status is broadcast to all CPUs; all CPUs in the package increment
this count. This is a 64-bit counter.
``core_throttle_max_time_ms``
Shows the maximum amount of time for which "Thermal Status flag" has been
set to 1 for this CPU at the core level since OS boot and thermal vector
is initialized.
``package_throttle_max_time_ms``
Shows the maximum amount of time for which "Thermal Status flag" has been
set to 1 for the package containing this CPU since OS boot and thermal
vector is initialized.
``core_throttle_total_time_ms``
Shows the cumulative time for which "Thermal Status flag" has been
set to 1 for this CPU for core level since OS boot and thermal vector
is initialized.
``package_throttle_total_time_ms``
Shows the cumulative time for which "Thermal Status flag" has been set
to 1 for the package containing this CPU since OS boot and thermal vector
is initialized.

View File

@ -0,0 +1,87 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/fsl,imx91-tmu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP i.MX91 Thermal
maintainers:
- Pengfei Li <pengfei.li_1@nxp.com>
description:
i.MX91 features a new temperature sensor. It includes programmable
temperature threshold comparators for both normal and privileged
accesses and allows a programmable measurement frequency for the
Periodic One-Shot Measurement mode. Additionally, it provides
status registers for indicating the end of measurement and threshold
violation events.
properties:
compatible:
items:
- const: fsl,imx91-tmu
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
items:
- description: Comparator 1 irq
- description: Comparator 2 irq
- description: Data ready irq
interrupt-names:
items:
- const: thr1
- const: thr2
- const: ready
nvmem-cells:
items:
- description: Phandle to the trim control 1 provided by ocotp
- description: Phandle to the trim control 2 provided by ocotp
nvmem-cell-names:
items:
- const: trim1
- const: trim2
"#thermal-sensor-cells":
const: 0
required:
- compatible
- reg
- clocks
- interrupts
- interrupt-names
allOf:
- $ref: thermal-sensor.yaml
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/imx93-clock.h>
thermal-sensor@44482000 {
compatible = "fsl,imx91-tmu";
reg = <0x44482000 0x1000>;
#thermal-sensor-cells = <0>;
clocks = <&clk IMX93_CLK_TMC_GATE>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "thr1", "thr2", "ready";
nvmem-cells = <&tmu_trim1>, <&tmu_trim2>;
nvmem-cell-names = "trim1", "trim2";
};
...

View File

@ -36,10 +36,15 @@ properties:
- qcom,msm8974-tsens
- const: qcom,tsens-v0_1
- description:
v1 of TSENS without RPM which requires to be explicitly reset
and enabled in the driver.
enum:
- qcom,ipq5018-tsens
- description: v1 of TSENS
items:
- enum:
- qcom,ipq5018-tsens
- qcom,msm8937-tsens
- qcom,msm8956-tsens
- qcom,msm8976-tsens
@ -50,11 +55,13 @@ properties:
items:
- enum:
- qcom,glymur-tsens
- qcom,kaanapali-tsens
- qcom,milos-tsens
- qcom,msm8953-tsens
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,qcm2290-tsens
- qcom,qcs8300-tsens
- qcom,qcs615-tsens
- qcom,sa8255p-tsens
- qcom,sa8775p-tsens

View File

@ -16,7 +16,11 @@ description:
properties:
compatible:
const: renesas,r9a09g047-tsu
oneOf:
- const: renesas,r9a09g047-tsu # RZ/G3E
- items:
- const: renesas,r9a09g057-tsu # RZ/V2H
- const: renesas,r9a09g047-tsu # RZ/G3E
reg:
maxItems: 1

View File

@ -409,3 +409,26 @@ based on the processor generation.
Limit 1 from being exhausted.
4 Unknown: Can't classify.
On processors starting from Panther Lake additional hints are provided.
The hardware analyzes workload residencies over an extended period to
determine whether the workload classification tends toward idle/battery
life states or sustained/performance states. Based on this long-term
analysis, it classifies:
Power Classification: If the workload exhibits more idle or battery life
residencies, it is classified as "power".
Performance Classification: If the workload exhibits more sustained or
performance residencies, it is classified as "performance".
This approach enables applications to ignore short-term workload
fluctuations and instead respond to longer-term power vs. performance
trends.
Residency thresholds for this classification are CPU generation-specific.
Classification is reported via bit 4 of the workload_type_index:
Bit 4 = 1: Power classification
Bit 4 = 0: Performance classification

View File

@ -1,4 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ACPI) += int340x_thermal.o
obj-$(CONFIG_DPTF_POWER) += dptf_power.o
obj-$(CONFIG_DPTF_PCH_FIVR) += dptf_pch_fivr.o

View File

@ -240,6 +240,8 @@ static const struct acpi_device_id int3407_device_ids[] = {
{"INTC10D9", 0},
{"INTC1100", 0},
{"INTC1101", 0},
{"INTC10F7", 0},
{"INTC10F8", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3407_device_ids);

View File

@ -1,94 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ACPI support for int340x thermal drivers
*
* Copyright (C) 2014, Intel Corporation
* Authors: Zhang Rui <rui.zhang@intel.com>
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include "../internal.h"
#define INT3401_DEVICE 0X01
static const struct acpi_device_id int340x_thermal_device_ids[] = {
{"INT3400"},
{"INT3401", INT3401_DEVICE},
{"INT3402"},
{"INT3403"},
{"INT3404"},
{"INT3406"},
{"INT3407"},
{"INT3408"},
{"INT3409"},
{"INT340A"},
{"INT340B"},
{"INT3532"},
{"INTC1040"},
{"INTC1041"},
{"INTC1042"},
{"INTC1043"},
{"INTC1044"},
{"INTC1045"},
{"INTC1046"},
{"INTC1047"},
{"INTC1048"},
{"INTC1049"},
{"INTC1050"},
{"INTC1060"},
{"INTC1061"},
{"INTC1062"},
{"INTC1063"},
{"INTC1064"},
{"INTC1065"},
{"INTC1066"},
{"INTC1068"},
{"INTC1069"},
{"INTC106A"},
{"INTC106B"},
{"INTC106C"},
{"INTC106D"},
{"INTC10A0"},
{"INTC10A1"},
{"INTC10A2"},
{"INTC10A3"},
{"INTC10A4"},
{"INTC10A5"},
{"INTC10D4"},
{"INTC10D5"},
{"INTC10D6"},
{"INTC10D7"},
{"INTC10D8"},
{"INTC10D9"},
{"INTC10FC"},
{"INTC10FD"},
{"INTC10FE"},
{"INTC10FF"},
{"INTC1100"},
{"INTC1101"},
{"INTC1102"},
{""},
};
static int int340x_thermal_handler_attach(struct acpi_device *adev,
const struct acpi_device_id *id)
{
if (IS_ENABLED(CONFIG_INT340X_THERMAL))
acpi_create_platform_device(adev, NULL);
/* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */
else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) &&
id->driver_data == INT3401_DEVICE)
acpi_create_platform_device(adev, NULL);
return 1;
}
static struct acpi_scan_handler int340x_thermal_handler = {
.ids = int340x_thermal_device_ids,
.attach = int340x_thermal_handler_attach,
};
void __init acpi_int340x_thermal_init(void)
{
acpi_scan_add_handler(&int340x_thermal_handler);
}

View File

@ -22,6 +22,7 @@
{"INTC10A2", }, /* Fan for Raptor Lake generation */ \
{"INTC10D6", }, /* Fan for Panther Lake generation */ \
{"INTC10FE", }, /* Fan for Wildcat Lake generation */ \
{"INTC10F5", }, /* Fan for Nova Lake generation */ \
{"PNP0C0B", } /* Generic ACPI fan */
#define ACPI_FPS_NAME_LEN 20

View File

@ -27,7 +27,6 @@ static inline void acpi_pci_link_init(void) {}
void acpi_processor_init(void);
void acpi_platform_init(void);
void acpi_pnp_init(void);
void acpi_int340x_thermal_init(void);
int acpi_sysfs_init(void);
void acpi_gpe_apply_masked_gpes(void);
void acpi_container_init(void);

View File

@ -2711,7 +2711,6 @@ void __init acpi_scan_init(void)
acpi_watchdog_init();
acpi_pnp_init();
acpi_power_resources_init();
acpi_int340x_thermal_init();
acpi_init_lpit();
acpi_scan_add_handler(&generic_device_handler);

View File

@ -296,6 +296,16 @@ config IMX8MM_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the passive
trip is crossed.
config IMX91_THERMAL
tristate "Temperature sensor driver for NXP i.MX91 SoC"
depends on ARCH_MXC || COMPILE_TEST
depends on OF
help
Include one sensor and six comparators. Each of them compares the
temperature value (from the sensor) against the programmable
threshold values. The direction of the comparison is configurable
(greater / lesser than).
config K3_THERMAL
tristate "Texas Instruments K3 thermal support"
depends on ARCH_K3 || COMPILE_TEST

View File

@ -51,6 +51,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_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_IMX91_THERMAL) += imx91_thermal.o
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o

View File

@ -0,0 +1,384 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2025 NXP.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/nvmem-consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/thermal.h>
#include <linux/units.h>
#define REG_SET 0x4
#define REG_CLR 0x8
#define REG_TOG 0xc
#define IMX91_TMU_CTRL0 0x0
#define IMX91_TMU_CTRL0_THR1_IE BIT(9)
#define IMX91_TMU_CTRL0_THR1_MASK GENMASK(3, 2)
#define IMX91_TMU_CTRL0_CLR_FLT1 BIT(21)
#define IMX91_TMU_THR_MODE_LE 0
#define IMX91_TMU_THR_MODE_GE 1
#define IMX91_TMU_STAT0 0x10
#define IMX91_TMU_STAT0_THR1_IF BIT(9)
#define IMX91_TMU_STAT0_THR1_STAT BIT(13)
#define IMX91_TMU_STAT0_DRDY0_IF_MASK BIT(16)
#define IMX91_TMU_DATA0 0x20
#define IMX91_TMU_CTRL1 0x200
#define IMX91_TMU_CTRL1_EN BIT(31)
#define IMX91_TMU_CTRL1_START BIT(30)
#define IMX91_TMU_CTRL1_STOP BIT(29)
#define IMX91_TMU_CTRL1_RES_MASK GENMASK(19, 18)
#define IMX91_TMU_CTRL1_MEAS_MODE_MASK GENMASK(25, 24)
#define IMX91_TMU_CTRL1_MEAS_MODE_SINGLE 0
#define IMX91_TMU_CTRL1_MEAS_MODE_CONTINUES 1
#define IMX91_TMU_CTRL1_MEAS_MODE_PERIODIC 2
#define IMX91_TMU_THR_CTRL01 0x30
#define IMX91_TMU_THR_CTRL01_THR1_MASK GENMASK(31, 16)
#define IMX91_TMU_REF_DIV 0x280
#define IMX91_TMU_DIV_EN BIT(31)
#define IMX91_TMU_DIV_MASK GENMASK(23, 16)
#define IMX91_TMU_DIV_MAX 255
#define IMX91_TMU_PUD_ST_CTRL 0x2b0
#define IMX91_TMU_PUDL_MASK GENMASK(23, 16)
#define IMX91_TMU_TRIM1 0x2e0
#define IMX91_TMU_TRIM2 0x2f0
#define IMX91_TMU_TEMP_LOW_LIMIT -40000
#define IMX91_TMU_TEMP_HIGH_LIMIT 125000
#define IMX91_TMU_DEFAULT_TRIM1_CONFIG 0xb561bc2d
#define IMX91_TMU_DEFAULT_TRIM2_CONFIG 0x65d4
#define IMX91_TMU_PERIOD_CTRL 0x270
#define IMX91_TMU_PERIOD_CTRL_MEAS_MASK GENMASK(23, 0)
#define IMX91_TMP_FRAC 64
struct imx91_tmu {
void __iomem *base;
struct clk *clk;
struct device *dev;
struct thermal_zone_device *tzd;
};
static void imx91_tmu_start(struct imx91_tmu *tmu, bool start)
{
u32 val = start ? IMX91_TMU_CTRL1_START : IMX91_TMU_CTRL1_STOP;
writel_relaxed(val, tmu->base + IMX91_TMU_CTRL1 + REG_SET);
}
static void imx91_tmu_enable(struct imx91_tmu *tmu, bool enable)
{
u32 reg = IMX91_TMU_CTRL1;
reg += enable ? REG_SET : REG_CLR;
writel_relaxed(IMX91_TMU_CTRL1_EN, tmu->base + reg);
}
static int imx91_tmu_to_mcelsius(int x)
{
return x * MILLIDEGREE_PER_DEGREE / IMX91_TMP_FRAC;
}
static int imx91_tmu_from_mcelsius(int x)
{
return x * IMX91_TMP_FRAC / MILLIDEGREE_PER_DEGREE;
}
static int imx91_tmu_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
s16 data;
/* DATA0 is 16bit signed number */
data = readw_relaxed(tmu->base + IMX91_TMU_DATA0);
*temp = imx91_tmu_to_mcelsius(data);
return 0;
}
static int imx91_tmu_set_trips(struct thermal_zone_device *tz, int low, int high)
{
struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
int val;
if (high >= IMX91_TMU_TEMP_HIGH_LIMIT)
return -EINVAL;
writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
/* Comparator1 for temperature threshold */
writel_relaxed(IMX91_TMU_THR_CTRL01_THR1_MASK, tmu->base + IMX91_TMU_THR_CTRL01 + REG_CLR);
val = FIELD_PREP(IMX91_TMU_THR_CTRL01_THR1_MASK, imx91_tmu_from_mcelsius(high));
writel_relaxed(val, tmu->base + IMX91_TMU_THR_CTRL01 + REG_SET);
writel_relaxed(IMX91_TMU_STAT0_THR1_IF, tmu->base + IMX91_TMU_STAT0 + REG_CLR);
writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_SET);
return 0;
}
static int imx91_init_from_nvmem_cells(struct imx91_tmu *tmu)
{
struct device *dev = tmu->dev;
u32 trim1, trim2;
int ret;
ret = nvmem_cell_read_u32(dev, "trim1", &trim1);
if (ret)
return ret;
ret = nvmem_cell_read_u32(dev, "trim2", &trim2);
if (ret)
return ret;
if (trim1 == 0 || trim2 == 0)
return -EINVAL;
writel_relaxed(trim1, tmu->base + IMX91_TMU_TRIM1);
writel_relaxed(trim2, tmu->base + IMX91_TMU_TRIM2);
return 0;
}
static void imx91_tmu_action_remove(void *data)
{
struct imx91_tmu *tmu = data;
/* disable tmu */
imx91_tmu_enable(tmu, false);
}
static irqreturn_t imx91_tmu_alarm_irq(int irq, void *data)
{
struct imx91_tmu *tmu = data;
u32 val;
val = readl_relaxed(tmu->base + IMX91_TMU_STAT0);
/* Check if comparison interrupt occurred */
if (val & IMX91_TMU_STAT0_THR1_IF) {
/* Clear irq flag and disable interrupt until reconfigured */
writel(IMX91_TMU_STAT0_THR1_IF, tmu->base + IMX91_TMU_STAT0 + REG_CLR);
writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
return IRQ_WAKE_THREAD;
}
return IRQ_NONE;
}
static irqreturn_t imx91_tmu_alarm_irq_thread(int irq, void *data)
{
struct imx91_tmu *tmu = data;
thermal_zone_device_update(tmu->tzd, THERMAL_EVENT_UNSPECIFIED);
return IRQ_HANDLED;
}
static int imx91_tmu_change_mode(struct thermal_zone_device *tz, enum thermal_device_mode mode)
{
struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
int ret;
if (mode == THERMAL_DEVICE_ENABLED) {
ret = pm_runtime_get(tmu->dev);
if (ret < 0)
return ret;
writel_relaxed(IMX91_TMU_CTRL0_THR1_IE | IMX91_TMU_CTRL0_THR1_MASK,
tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL0_THR1_MASK, IMX91_TMU_THR_MODE_GE),
tmu->base + IMX91_TMU_CTRL0 + REG_SET);
imx91_tmu_start(tmu, true);
} else {
writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
imx91_tmu_start(tmu, false);
pm_runtime_put(tmu->dev);
}
return 0;
}
static struct thermal_zone_device_ops tmu_tz_ops = {
.get_temp = imx91_tmu_get_temp,
.change_mode = imx91_tmu_change_mode,
.set_trips = imx91_tmu_set_trips,
};
static int imx91_tmu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct imx91_tmu *tmu;
unsigned long rate;
int irq, ret;
u32 div;
tmu = devm_kzalloc(dev, sizeof(struct imx91_tmu), GFP_KERNEL);
if (!tmu)
return -ENOMEM;
tmu->dev = dev;
tmu->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tmu->base))
return dev_err_probe(dev, PTR_ERR(tmu->base), "failed to get io resource");
tmu->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(tmu->clk))
return dev_err_probe(dev, PTR_ERR(tmu->clk), "failed to get tmu clock\n");
platform_set_drvdata(pdev, tmu);
/* disable the monitor during initialization */
imx91_tmu_enable(tmu, false);
imx91_tmu_start(tmu, false);
ret = imx91_init_from_nvmem_cells(tmu);
if (ret) {
dev_warn(dev, "can't get trim value, use default settings\n");
writel_relaxed(IMX91_TMU_DEFAULT_TRIM1_CONFIG, tmu->base + IMX91_TMU_TRIM1);
writel_relaxed(IMX91_TMU_DEFAULT_TRIM2_CONFIG, tmu->base + IMX91_TMU_TRIM2);
}
/* The typical conv clk is 4MHz, the output freq is 'rate / (div + 1)' */
rate = clk_get_rate(tmu->clk);
div = (rate / (4 * HZ_PER_MHZ)) - 1;
if (div > IMX91_TMU_DIV_MAX)
return dev_err_probe(dev, -EINVAL, "clock divider exceed hardware limitation");
/* Set divider value and enable divider */
writel_relaxed(IMX91_TMU_DIV_EN | FIELD_PREP(IMX91_TMU_DIV_MASK, div),
tmu->base + IMX91_TMU_REF_DIV);
/* Set max power up delay: 'Tpud(ms) = 0xFF * 1000 / 4000000' */
writel_relaxed(FIELD_PREP(IMX91_TMU_PUDL_MASK, 100U), tmu->base + IMX91_TMU_PUD_ST_CTRL);
/*
* Set resolution mode
* 00b - Conversion time = 0.59325 ms
* 01b - Conversion time = 1.10525 ms
* 10b - Conversion time = 2.12925 ms
* 11b - Conversion time = 4.17725 ms
*/
writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_RES_MASK, 0x3),
tmu->base + IMX91_TMU_CTRL1 + REG_CLR);
writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_RES_MASK, 0x1),
tmu->base + IMX91_TMU_CTRL1 + REG_SET);
writel_relaxed(IMX91_TMU_CTRL1_MEAS_MODE_MASK, tmu->base + IMX91_TMU_CTRL1 + REG_CLR);
writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_MEAS_MODE_MASK,
IMX91_TMU_CTRL1_MEAS_MODE_PERIODIC),
tmu->base + IMX91_TMU_CTRL1 + REG_SET);
/*
* Set Periodic Measurement Frequency to 25Hz:
* tMEAS_FREQ = tCONV_CLK * PERIOD_CTRL[MEAS_FREQ]
*/
writel_relaxed(FIELD_PREP(IMX91_TMU_PERIOD_CTRL_MEAS_MASK, 4 * HZ_PER_MHZ / 25),
tmu->base + IMX91_TMU_PERIOD_CTRL);
imx91_tmu_enable(tmu, true);
ret = devm_add_action(dev, imx91_tmu_action_remove, tmu);
if (ret)
return dev_err_probe(dev, ret, "Failure to add action imx91_tmu_action_remove()\n");
pm_runtime_set_active(dev);
pm_runtime_get_noresume(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
tmu->tzd = devm_thermal_of_zone_register(dev, 0, tmu, &tmu_tz_ops);
if (IS_ERR(tmu->tzd))
return dev_err_probe(dev, PTR_ERR(tmu->tzd),
"failed to register thermal zone sensor\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(dev, irq, imx91_tmu_alarm_irq,
imx91_tmu_alarm_irq_thread,
IRQF_ONESHOT, "imx91_thermal", tmu);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to request alarm irq\n");
pm_runtime_put(dev);
return 0;
}
static int imx91_tmu_runtime_suspend(struct device *dev)
{
struct imx91_tmu *tmu = dev_get_drvdata(dev);
/* disable tmu */
imx91_tmu_enable(tmu, false);
clk_disable_unprepare(tmu->clk);
return 0;
}
static int imx91_tmu_runtime_resume(struct device *dev)
{
struct imx91_tmu *tmu = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(tmu->clk);
if (ret)
return ret;
imx91_tmu_enable(tmu, true);
return 0;
}
static DEFINE_RUNTIME_DEV_PM_OPS(imx91_tmu_pm_ops, imx91_tmu_runtime_suspend,
imx91_tmu_runtime_resume, NULL);
static const struct of_device_id imx91_tmu_table[] = {
{ .compatible = "fsl,imx91-tmu", },
{ },
};
MODULE_DEVICE_TABLE(of, imx91_tmu_table);
static struct platform_driver imx91_tmu = {
.driver = {
.name = "imx91_thermal",
.pm = pm_ptr(&imx91_tmu_pm_ops),
.of_match_table = imx91_tmu_table,
},
.probe = imx91_tmu_probe,
};
module_platform_driver(imx91_tmu);
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
MODULE_DESCRIPTION("i.MX91 Thermal Monitor Unit driver");
MODULE_LICENSE("GPL");

View File

@ -44,7 +44,8 @@ config INTEL_SOC_DTS_IOSF_CORE
config INTEL_SOC_DTS_THERMAL
tristate "Intel SoCs DTS thermal driver"
depends on X86 && PCI && ACPI
depends on X86_64 && PCI && ACPI && NET
select INT340X_THERMAL
select INTEL_SOC_DTS_IOSF_CORE
help
Enable this to register Intel SoCs (e.g. Bay Trail) platform digital

View File

@ -16,6 +16,8 @@
#define INT3400_ODVP_CHANGED 0x88
#define INT3400_KEEP_ALIVE 0xA0
#define INT3400_FAKE_TEMP (20 * 1000) /* faked temp sensor with 20C */
/* UUID prefix length for comparison - sufficient for all UUIDs */
#define INT3400_UUID_PREFIX_LEN 7
enum int3400_thermal_uuid {
INT3400_THERMAL_ACTIVE = 0,
@ -112,7 +114,7 @@ static ssize_t available_uuids_show(struct device *dev,
int length = 0;
if (!priv->uuid_bitmap)
return sprintf(buf, "UNKNOWN\n");
return sysfs_emit(buf, "UNKNOWN\n");
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
if (priv->uuid_bitmap & (1 << i))
@ -129,7 +131,7 @@ static ssize_t current_uuid_show(struct device *dev,
int i, length = 0;
if (priv->current_uuid_index >= 0)
return sprintf(buf, "%s\n",
return sysfs_emit(buf, "%s\n",
int3400_thermal_uuids[priv->current_uuid_index]);
for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
@ -140,7 +142,7 @@ static ssize_t current_uuid_show(struct device *dev,
if (length)
return length;
return sprintf(buf, "INVALID\n");
return sysfs_emit(buf, "INVALID\n");
}
static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable)
@ -199,7 +201,7 @@ static ssize_t current_uuid_store(struct device *dev,
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
if (!strncmp(buf, int3400_thermal_uuids[i],
sizeof(int3400_thermal_uuids[i]) - 1)) {
INT3400_UUID_PREFIX_LEN)) {
/*
* If we have a list of supported UUIDs, make sure
* this one is supported.
@ -340,7 +342,7 @@ static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
odvp_attr = container_of(attr, struct odvp_attr, attr);
return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
return sysfs_emit(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
}
static void cleanup_odvp(struct int3400_thermal_priv *priv)
@ -691,6 +693,7 @@ static const struct acpi_device_id int3400_thermal_match[] = {
{"INTC10A0", 0},
{"INTC10D4", 0},
{"INTC10FC", 0},
{"INTC10F3", 0},
{}
};

View File

@ -277,6 +277,7 @@ static const struct acpi_device_id int3403_device_ids[] = {
{"INTC10A1", 0},
{"INTC10D5", 0},
{"INTC10FD", 0},
{"INTC10F4", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);

View File

@ -27,6 +27,8 @@
#define PCI_DEVICE_ID_INTEL_JSL_THERMAL 0x4E03
#define PCI_DEVICE_ID_INTEL_LNLM_THERMAL 0x641D
#define PCI_DEVICE_ID_INTEL_MTLP_THERMAL 0x7D03
#define PCI_DEVICE_ID_INTEL_NVL_H_THERMAL 0xD703
#define PCI_DEVICE_ID_INTEL_NVL_S_THERMAL 0xAD03
#define PCI_DEVICE_ID_INTEL_RPL_THERMAL 0xA71D
#define PCI_DEVICE_ID_INTEL_SKL_THERMAL 0x1903
#define PCI_DEVICE_ID_INTEL_TGL_THERMAL 0x9A03

View File

@ -504,6 +504,16 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR |
PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT |
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) },
{ PCI_DEVICE_DATA(INTEL, NVL_H_THERMAL, PROC_THERMAL_FEATURE_RAPL |
PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
{ PCI_DEVICE_DATA(INTEL, NVL_S_THERMAL, PROC_THERMAL_FEATURE_RAPL |
PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
{ },
};

View File

@ -87,6 +87,17 @@ static const struct mapping_table lnl_dlvr_mapping[] = {
{NULL, 0, NULL},
};
static const struct mmio_reg nvl_dlvr_mmio_regs[] = {
{ 0, 0x19208, 5, 0x1F, 0}, /* dlvr_spread_spectrum_pct */
{ 0, 0x19208, 1, 0x1, 5}, /* dlvr_control_mode */
{ 0, 0x19208, 1, 0x1, 6}, /* dlvr_control_lock */
{ 0, 0x19208, 1, 0x1, 7}, /* dlvr_rfim_enable */
{ 0, 0x19208, 12, 0xFFF, 8}, /* dlvr_freq_select */
{ 1, 0x19210, 2, 0x3, 30}, /* dlvr_hardware_rev */
{ 1, 0x19210, 16, 0xFFFF, 0}, /* dlvr_freq_mhz */
{ 1, 0x19210, 1, 0x1, 16}, /* dlvr_pll_busy */
};
static int match_mapping_table(const struct mapping_table *table, const char *attr_name,
bool match_int_value, const u32 value, const char *value_str,
char **result_str, u32 *result_int)
@ -446,6 +457,10 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc
dlvr_mmio_regs_table = lnl_dlvr_mmio_regs;
dlvr_mapping = lnl_dlvr_mapping;
break;
case PCI_DEVICE_ID_INTEL_NVL_H_THERMAL:
case PCI_DEVICE_ID_INTEL_NVL_S_THERMAL:
dlvr_mmio_regs_table = nvl_dlvr_mmio_regs;
break;
default:
dlvr_mmio_regs_table = dlvr_mmio_regs;
break;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* R-Car Gen3 THS thermal sensor driver
* R-Car Gen3, Gen4 and RZ/G2 THS thermal sensor driver
* Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen.
*
* Copyright (C) 2016 Renesas Electronics Corporation.
@ -601,7 +601,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
return ret;
}
static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
static int rcar_gen3_thermal_resume(struct device *dev)
{
struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
unsigned int i;
@ -615,13 +615,13 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
rcar_gen3_thermal_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
rcar_gen3_thermal_resume);
static struct platform_driver rcar_gen3_thermal_driver = {
.driver = {
.name = "rcar_gen3_thermal",
.pm = &rcar_gen3_thermal_pm_ops,
.pm = pm_sleep_ptr(&rcar_gen3_thermal_pm_ops),
.of_match_table = rcar_gen3_thermal_dt_ids,
},
.probe = rcar_gen3_thermal_probe,

View File

@ -534,7 +534,6 @@ static int rcar_thermal_probe(struct platform_device *pdev)
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int rcar_thermal_suspend(struct device *dev)
{
struct rcar_thermal_common *common = dev_get_drvdata(dev);
@ -567,15 +566,14 @@ static int rcar_thermal_resume(struct device *dev)
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend,
rcar_thermal_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend,
rcar_thermal_resume);
static struct platform_driver rcar_thermal_driver = {
.driver = {
.name = "rcar_thermal",
.pm = &rcar_thermal_pm_ops,
.pm = pm_sleep_ptr(&rcar_thermal_pm_ops),
.of_match_table = rcar_thermal_dt_ids,
},
.probe = rcar_thermal_probe,

View File

@ -374,7 +374,7 @@ int main(int argc, char *argv[])
}
if (options.daemonize && daemon(0, 0)) {
ERROR("Failed to daemonize: %p\n");
ERROR("Failed to daemonize: %m\n");
return THERMAL_ENGINE_DAEMON_ERROR;
}