* [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU
@ 2026-06-02 17:00 Tudor Ambarus
2026-06-02 17:00 ` [PATCH v7 1/5] dt-bindings: thermal: Add " Tudor Ambarus
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Tudor Ambarus @ 2026-06-02 17:00 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook,
Gustavo A. R. Silva, Peter Griffin, André Draszik,
Alim Akhtar
Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree,
linux-hardening, linux-arm-kernel, Tudor Ambarus,
Krzysztof Kozlowski
Add support for the Thermal Management Unit (TMU) on the Google GS101
SoC.
The GS101 TMU implementation utilizes a hybrid architecture where
management is shared between the kernel and the Alive Clock and
Power Manager (ACPM) firmware. This hybrid ACPM TMU architecture is
also present on other Samsung Exynos SoCs (e.g., AutoV920, Exynos850).
Dependencies
============
The set depends on the ACPM TMU firmware helper driver that was queued
via the Samsung SoC tree:
https://lore.kernel.org/r/20260531115713.19388-2-krzk@kernel.org/T/#u
The bindings, driver and MAINTAINERS are expected to go via the thermal
tree. The dts and defconfig patches are expected to go via the Samsung
SoC tree, after the bindings and driver are queued.
Architecture Overview
=====================
The hardware supports two parallel control paths. For this
implementation, responsibilities are split as follows:
1. Kernel Responsibility:
- maintain direct memory-mapped access to the interrupt pending
(INTPEND) registers to identify thermal events.
- map physical hardware interrupts to logical thermal zones.
- coordinate functional operations through the ACPM IPC protocol.
2. Firmware Responsibility (ACPM):
- handle sensor initialization.
- manage thermal thresholds configuration.
- perform temperature acquisition and expose data via IPC.
Sensor Mapping (One-to-Many)
============================
The SoC contains multiple physical temperature sensors, but the ACPM
firmware abstracts these into logical groups (Clusters) for reporting:
- ACPM Sensor 0 (Big Cluster): Aggregates physical sensors 0, 6, 7, 8, 9.
- ACPM Sensor 1 (Mid Cluster): Aggregates physical sensors 4, 5.
- ACPM Sensor 2 (Little Cluster): Aggregates physical sensors 1, 2.
The driver maps physical interrupt bits back to these logical parents.
When an interrupt fires, the driver checks the bitmask in the INTPEND
registers and updates the corresponding logical thermal zone.
Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
---
Changes in v7, addressed Sashiko's driver review:
- add a rollback mechanism in `acpm_tmu_control` to prevent leaving hardware in
a dirty state when we fail to enable a sensor midway during probe or
resume.
- prevent calling `ops->read_temp` on a disabled sensor if a concurrent disable
occurs (e.g. suspend).
- don't mix mutex guards with goto as per cleanup guidelines
- fix irq clear order in `acpm_tmu_thread_fn`. Avoid clearing a new interrupt
that occurs during `thermal_zone_device_update` (which can cause missed
over-temperature events).
- initialize hardware thresholds at boot and prevent boot-time spurious
interrupts (which previously triggered because sensors were enabled before
thresholds were set).
- ensure clocks are disabled when removing the driver, even if an autosuspend
is pending (which would otherwise get cancelled by `pm_runtime_disable()`,
leaking the clock).
- add rollback in `acpm_tmu_suspend` on failure. If system suspend fails at
`ops->suspend`, re-enable the TMU sensors so they don't remain disabled.
- Link to v6: https://lore.kernel.org/r/20260528-acpm-tmu-v6-0-b4d7ce57594b@linaro.org
Changes in v6:
- defconfig: reword commit message (Krzysztof).
- bindings: make samsung,acpm-ipc a required property (Krzysztof,
sashiko)
- address sashiko's review feedback on the driver:
- ensure that the loop continues to disable the rest of the sensors
on the teardown path in `acpm_tmu_control`.
- avoid leaving the hardware in an active state during remove by
s/pm_runtime_put_autosuspend(dev)/pm_runtime_put_sync(dev) in
`acpm_tmu_control`.
- initialize `acpm_temp` in `acpm_tmu_get_temp` to mitigate the risk
of exposing stack memory.
- fix race condition in `acpm_tmu_update_thresholds`. Guard
`was_enabled = sensor->enabled;` with the mutex held.
- prevent keeping the sensor disabled on a threshold setting error
scenario.
- allow the IRQ subsystem to correctly detect an interrupt storm by
returning IRQ_NONE.
- devres ordering in probe, eliminate a UAF by moving the IRQ request
after registering the zones.
- self review on Kconfig:
- add `depends on HAS_IOMEM`, due to `devm_platform_ioremap_resource`
- add `default ARCH_EXYNOS`, based on Krzysztof's patch from:
Link: https://lore.kernel.org/linux-samsung-soc/b20c560f-4bc3-4686-9c91-36d93f1535b2@oss.qualcomm.com/T/#t
- Link to v5: https://lore.kernel.org/r/20260525-acpm-tmu-v5-0-85fde739752e@linaro.org
Changes in v5:
- no changes, rebase on top of krzk/for-next branch.
- Link to v4: https://lore.kernel.org/r/20260423-acpm-tmu-v4-0-8b59f8548634@linaro.org
Changes in v4: address sashiko review:
- thermal driver: avoid mixing mutex cleanup helpers with goto statements
- firmware, tmu:
- remove __packed from union acpm_tmu_msg.
- return ERR_PTR(-ENODEV) for devm_acpm_get_by_phandle when
CONFIG_EXYNOS_ACPM_PROTOCOL is disabled.
- Link to v3: https://lore.kernel.org/r/20260420-acpm-tmu-v3-0-3dc8e93f0b26@linaro.org
Changes in v3:
- thermal driver: use .set_trips() instead of .set_trip_point()
- new cleaning/prerequisite patches for firmware/acpm:
- firmware: samsung: acpm: Make acpm_ops const and access via pointer
- firmware: samsung: acpm: Drop redundant _ops suffix in acpm_ops members
- firmware: samsung: acpm: Consolidate transfer initialization helper
- firmware: acpm: TMU helpers - check return value from the firmware
- overall change: emphasize that the ACPM TMU hibrid approach applies to
other Samsung SoCs as well (Exynos850, AutoV920).
- dts: drop active trip points, update trip point values
- collect R-b tags
- Link to v2: https://lore.kernel.org/r/20260119-acpm-tmu-v2-0-e02a834f04c6@linaro.org
Changes in v2:
- architecture: switch from a syscon/MFD approach to a thermal-sensor
node with a phandle to the ACPM interface
- bindings: address Krzysztof's feedback, drop redundencies,
interrupts description.
- firmware: introduce devm_acpm_get_by_phandle() to standardize IPC
handle acquisition.
- thermal driver: drop compatible's data and use the static data from
the driver directly.
- defconfig, make EXYNOS_ACPM_THERMAL a module
- Link to v1: https://lore.kernel.org/r/20260114-acpm-tmu-v1-0-cfe56d93e90f@linaro.org
---
Tudor Ambarus (5):
dt-bindings: thermal: Add Google GS101 TMU
thermal: samsung: Add Exynos ACPM TMU driver GS101
MAINTAINERS: Add entry for Samsung Exynos ACPM thermal driver
arm64: dts: exynos: gs101: Add thermal management unit
arm64: defconfig: enable Exynos ACPM thermal support
.../bindings/thermal/google,gs101-tmu-top.yaml | 69 +++
MAINTAINERS | 8 +
arch/arm64/boot/dts/exynos/google/gs101-tmu.dtsi | 136 +++++
arch/arm64/boot/dts/exynos/google/gs101.dtsi | 18 +
arch/arm64/configs/defconfig | 1 +
drivers/thermal/samsung/Kconfig | 19 +
drivers/thermal/samsung/Makefile | 2 +
drivers/thermal/samsung/acpm-tmu.c | 618 +++++++++++++++++++++
8 files changed, 871 insertions(+)
---
base-commit: a1c3227fc1a1cd83cd7fceb93406da4e37fe06eb
change-id: 20260113-acpm-tmu-27e21f0e2c3b
Best regards,
--
Tudor Ambarus <tudor.ambarus@linaro.org>
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v7 1/5] dt-bindings: thermal: Add Google GS101 TMU 2026-06-02 17:00 [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU Tudor Ambarus @ 2026-06-02 17:00 ` Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 Tudor Ambarus ` (3 subsequent siblings) 4 siblings, 0 replies; 9+ messages in thread From: Tudor Ambarus @ 2026-06-02 17:00 UTC (permalink / raw) To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik, Alim Akhtar Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree, linux-hardening, linux-arm-kernel, Tudor Ambarus, Krzysztof Kozlowski Document the Thermal Management Unit (TMU) found on the Google GS101 SoC. The GS101 TMU utilizes a hybrid control model shared between the Application Processor (AP) and the ACPM (Alive Clock and Power Manager) firmware. This hybrid ACPM TMU architecture is also present on other Samsung Exynos SoCs (e.g., AutoV920, Exynos850). While the TMU is a standard memory-mapped IP block, on this platform the AP's direct register access is restricted to the interrupt pending (INTPEND) registers for event identification. High-level functional tasks, such as sensor initialization, threshold programming, and temperature reads, are delegated to the ACPM firmware. Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> --- .../bindings/thermal/google,gs101-tmu-top.yaml | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml b/Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml new file mode 100644 index 000000000000..75560ebca48d --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/google,gs101-tmu-top.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung Exynos ACPM Thermal Management Unit (TMU) + +maintainers: + - Tudor Ambarus <tudor.ambarus@linaro.org> + +description: + The Samsung Exynos ACPM TMU is a thermal sensor block found on Exynos + based platforms (such as Google GS101 and Exynos850). It supports + both direct register-level access and firmware-mediated management + via the ACPM (Alive Clock and Power Manager) firmware. + + On these platforms, the hardware is managed in a hybrid fashion. The + Application Processor (AP) maintains direct memory-mapped access + exclusively to the interrupt pending registers to identify thermal + events. All other functional aspects - including sensor + initialization, threshold configuration, and temperature acquisition + - are handled by the ACPM firmware. The AP coordinates these + operations through the ACPM IPC protocol. + +properties: + compatible: + const: google,gs101-tmu-top + + reg: + maxItems: 1 + + clocks: + items: + - description: APB peripheral clock (PCLK) for TMU register access. + + interrupts: + maxItems: 1 + + "#thermal-sensor-cells": + const: 1 + + samsung,acpm-ipc: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the ACPM IPC node. + +required: + - compatible + - reg + - clocks + - interrupts + - "#thermal-sensor-cells" + - samsung,acpm-ipc + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/google,gs101.h> + + thermal-sensor@100a0000 { + compatible = "google,gs101-tmu-top"; + reg = <0x100a0000 0x800>; + clocks = <&cmu_misc CLK_GOUT_MISC_TMU_TOP_PCLK>; + interrupts = <GIC_SPI 769 IRQ_TYPE_LEVEL_HIGH 0>; + #thermal-sensor-cells = <1>; + samsung,acpm-ipc = <&acpm_ipc>; + }; -- 2.54.0.1013.g208068f2d8-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 2026-06-02 17:00 [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 1/5] dt-bindings: thermal: Add " Tudor Ambarus @ 2026-06-02 17:00 ` Tudor Ambarus 2026-06-02 17:14 ` sashiko-bot 2026-06-02 19:49 ` Alexey Klimov 2026-06-02 17:00 ` [PATCH v7 3/5] MAINTAINERS: Add entry for Samsung Exynos ACPM thermal driver Tudor Ambarus ` (2 subsequent siblings) 4 siblings, 2 replies; 9+ messages in thread From: Tudor Ambarus @ 2026-06-02 17:00 UTC (permalink / raw) To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik, Alim Akhtar Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree, linux-hardening, linux-arm-kernel, Tudor Ambarus, Krzysztof Kozlowski Add driver for the Thermal Management Unit (TMU) managed via the Alive Clock and Power Manager (ACPM), found on Samsung Exynos SoCs such as Google GS101 (and Exynos850, autov920, etc.). The TMU on utilizes a hybrid management model shared between the Application Processor (AP) and the ACPM firmware. The driver maintains direct memory-mapped access to the TMU interrupt pending registers to identify thermal events, while delegating functional tasks - such as sensor initialization, threshold configuration, and temperature acquisition - to the ACPM firmware via the ACPM IPC protocol. Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> --- drivers/thermal/samsung/Kconfig | 19 ++ drivers/thermal/samsung/Makefile | 2 + drivers/thermal/samsung/acpm-tmu.c | 618 +++++++++++++++++++++++++++++++++++++ 3 files changed, 639 insertions(+) diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig index f4eff5a41a84..383ae3f56cbb 100644 --- a/drivers/thermal/samsung/Kconfig +++ b/drivers/thermal/samsung/Kconfig @@ -9,3 +9,22 @@ config EXYNOS_THERMAL the TMU, reports temperature and handles cooling action if defined. This driver uses the Exynos core thermal APIs and TMU configuration data from the supported SoCs. + +config EXYNOS_ACPM_THERMAL + tristate "Exynos ACPM thermal management unit driver" + depends on THERMAL_OF + depends on HAS_IOMEM + depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL) + default ARCH_EXYNOS + help + Support for the Thermal Management Unit (TMU) on Samsung Exynos SoCs + (such as Google GS101 and Exynos850). + + The TMU on these platforms is managed through a hybrid architecture. + This driver handles direct register access for thermal interrupt status + monitoring and communicates with the Alive Clock and Power Manager + (ACPM) firmware via the ACPM IPC protocol for functional sensor control + and configuration. + + Select this if you want to monitor device temperature and enable + thermal mitigation on Samsung Exynos ACPM based devices. diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile index f139407150d2..daed80647c34 100644 --- a/drivers/thermal/samsung/Makefile +++ b/drivers/thermal/samsung/Makefile @@ -4,3 +4,5 @@ # obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o exynos_thermal-y := exynos_tmu.o +obj-$(CONFIG_EXYNOS_ACPM_THERMAL) += exynos_acpm_thermal.o +exynos_acpm_thermal-y := acpm-tmu.o diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c new file mode 100644 index 000000000000..2de34b03b9ea --- /dev/null +++ b/drivers/thermal/samsung/acpm-tmu.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2019 Samsung Electronics Co., Ltd. + * Copyright 2025 Google LLC. + * Copyright 2026 Linaro Ltd. + */ + +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/firmware/samsung/exynos-acpm-protocol.h> +#include <linux/interrupt.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/thermal.h> +#include <linux/units.h> + +#include "../thermal_hwmon.h" + +#define EXYNOS_TMU_SENSOR(i) BIT(i) +#define EXYNOS_TMU_SENSORS_MAX_COUNT 16 + +#define GS101_CPUCL2_SENSOR_MASK (EXYNOS_TMU_SENSOR(0) | \ + EXYNOS_TMU_SENSOR(6) | \ + EXYNOS_TMU_SENSOR(7) | \ + EXYNOS_TMU_SENSOR(8) | \ + EXYNOS_TMU_SENSOR(9)) +#define GS101_CPUCL1_SENSOR_MASK (EXYNOS_TMU_SENSOR(4) | \ + EXYNOS_TMU_SENSOR(5)) +#define GS101_CPUCL0_SENSOR_MASK (EXYNOS_TMU_SENSOR(1) | \ + EXYNOS_TMU_SENSOR(2)) + +#define GS101_REG_INTPEND(i) ((i) * 0x50 + 0xf8) + +enum { + P0_INTPEND, + P1_INTPEND, + P2_INTPEND, + P3_INTPEND, + P4_INTPEND, + P5_INTPEND, + P6_INTPEND, + P7_INTPEND, + P8_INTPEND, + P9_INTPEND, + P10_INTPEND, + P11_INTPEND, + P12_INTPEND, + P13_INTPEND, + P14_INTPEND, + P15_INTPEND, + REG_INTPEND_COUNT, +}; + +struct acpm_tmu_sensor_group { + u16 mask; + u8 id; +}; + +struct acpm_tmu_sensor { + const struct acpm_tmu_sensor_group *group; + struct thermal_zone_device *tzd; + struct acpm_tmu_priv *priv; + struct mutex lock; /* protects sensor state */ + bool enabled; +}; + +struct acpm_tmu_priv { + struct regmap_field *regmap_fields[REG_INTPEND_COUNT]; + struct acpm_handle *handle; + struct device *dev; + struct clk *clk; + unsigned int mbox_chan_id; + unsigned int num_sensors; + int irq; + struct acpm_tmu_sensor sensors[] __counted_by(num_sensors); +}; + +struct acpm_tmu_driver_data { + const struct reg_field *reg_fields; + const struct acpm_tmu_sensor_group *sensor_groups; + unsigned int num_sensor_groups; + unsigned int mbox_chan_id; +}; + +#define ACPM_TMU_SENSOR_GROUP(_mask, _id) \ + { \ + .mask = _mask, \ + .id = _id, \ + } + +static const struct acpm_tmu_sensor_group gs101_sensor_groups[] = { + ACPM_TMU_SENSOR_GROUP(GS101_CPUCL2_SENSOR_MASK, 0), + ACPM_TMU_SENSOR_GROUP(GS101_CPUCL1_SENSOR_MASK, 1), + ACPM_TMU_SENSOR_GROUP(GS101_CPUCL0_SENSOR_MASK, 2), +}; + +static const struct reg_field gs101_reg_fields[REG_INTPEND_COUNT] = { + [P0_INTPEND] = REG_FIELD(GS101_REG_INTPEND(0), 0, 31), + [P1_INTPEND] = REG_FIELD(GS101_REG_INTPEND(1), 0, 31), + [P2_INTPEND] = REG_FIELD(GS101_REG_INTPEND(2), 0, 31), + [P3_INTPEND] = REG_FIELD(GS101_REG_INTPEND(3), 0, 31), + [P4_INTPEND] = REG_FIELD(GS101_REG_INTPEND(4), 0, 31), + [P5_INTPEND] = REG_FIELD(GS101_REG_INTPEND(5), 0, 31), + [P6_INTPEND] = REG_FIELD(GS101_REG_INTPEND(6), 0, 31), + [P7_INTPEND] = REG_FIELD(GS101_REG_INTPEND(7), 0, 31), + [P8_INTPEND] = REG_FIELD(GS101_REG_INTPEND(8), 0, 31), + [P9_INTPEND] = REG_FIELD(GS101_REG_INTPEND(9), 0, 31), + [P10_INTPEND] = REG_FIELD(GS101_REG_INTPEND(10), 0, 31), + [P11_INTPEND] = REG_FIELD(GS101_REG_INTPEND(11), 0, 31), + [P12_INTPEND] = REG_FIELD(GS101_REG_INTPEND(12), 0, 31), + [P13_INTPEND] = REG_FIELD(GS101_REG_INTPEND(13), 0, 31), + [P14_INTPEND] = REG_FIELD(GS101_REG_INTPEND(14), 0, 31), + [P15_INTPEND] = REG_FIELD(GS101_REG_INTPEND(15), 0, 31), +}; + +static const struct regmap_config gs101_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .use_relaxed_mmio = true, + .max_register = GS101_REG_INTPEND(15), +}; + +static const struct acpm_tmu_driver_data acpm_tmu_gs101 = { + .reg_fields = gs101_reg_fields, + .sensor_groups = gs101_sensor_groups, + .num_sensor_groups = ARRAY_SIZE(gs101_sensor_groups), + .mbox_chan_id = 9, +}; + +static int acpm_tmu_op_tz_control(struct acpm_tmu_sensor *sensor, bool on) +{ + struct acpm_tmu_priv *priv = sensor->priv; + struct acpm_handle *handle = priv->handle; + const struct acpm_tmu_ops *ops = &handle->ops->tmu; + int ret; + + ret = ops->tz_control(handle, priv->mbox_chan_id, sensor->group->id, + on); + if (ret) + return ret; + + sensor->enabled = on; + + return 0; +} + +static void acpm_tmu_control_rollback(struct acpm_tmu_priv *priv, int start_idx) +{ + int i; + + for (i = start_idx; i >= 0; i--) { + struct acpm_tmu_sensor *sensor = &priv->sensors[i]; + int ret; + + if (!sensor->tzd) + continue; + + mutex_lock(&sensor->lock); + ret = acpm_tmu_op_tz_control(sensor, false); + mutex_unlock(&sensor->lock); + if (ret) + dev_err(priv->dev, "Rollback: Failed to disable sensor %d: %d\n", + i, ret); + } +} + +static int acpm_tmu_control(struct acpm_tmu_priv *priv, bool on) +{ + struct device *dev = priv->dev; + int i, ret, err = 0; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + for (i = 0; i < priv->num_sensors; i++) { + struct acpm_tmu_sensor *sensor = &priv->sensors[i]; + + /* Skip sensors that weren't found in DT */ + if (!sensor->tzd) + continue; + + mutex_lock(&sensor->lock); + ret = acpm_tmu_op_tz_control(sensor, on); + mutex_unlock(&sensor->lock); + if (ret) { + if (!err) + err = ret; + + /* On enable, stop on first error. On disable, keep going */ + if (on) { + acpm_tmu_control_rollback(priv, i - 1); + break; + } + } + } + + pm_runtime_put_sync(dev); + + return err; +} + +static int acpm_tmu_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct acpm_tmu_sensor *sensor = thermal_zone_device_priv(tz); + struct acpm_tmu_priv *priv = sensor->priv; + struct acpm_handle *handle = priv->handle; + const struct acpm_tmu_ops *ops = &handle->ops->tmu; + struct device *dev = priv->dev; + int acpm_temp = 0, ret; + + /* + * Fast path: check locklessly if the sensor is enabled to avoid + * expensive runtime PM operations when it is disabled. Any race with + * concurrent disabling is caught by the second check under the lock + * after PM resume. + */ + if (!sensor->enabled) + return -EAGAIN; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + scoped_guard(mutex, &sensor->lock) { + if (!sensor->enabled) { + ret = -EAGAIN; + } else { + ret = ops->read_temp(handle, priv->mbox_chan_id, + sensor->group->id, &acpm_temp); + } + } + + pm_runtime_put_autosuspend(dev); + + if (ret) + return ret; + + *temp = acpm_temp * MILLIDEGREE_PER_DEGREE; + + return 0; +} + +static int acpm_tmu_update_thresholds(struct acpm_tmu_sensor *sensor, + u8 thresholds[2], u8 inten) +{ + struct acpm_tmu_priv *priv = sensor->priv; + struct acpm_handle *handle = priv->handle; + const struct acpm_tmu_ops *ops = &handle->ops->tmu; + unsigned int mbox_chan_id = priv->mbox_chan_id; + u8 acpm_sensor_id = sensor->group->id; + bool was_enabled; + int ret, restore_ret; + + guard(mutex)(&sensor->lock); + + was_enabled = sensor->enabled; + + if (was_enabled) { + ret = acpm_tmu_op_tz_control(sensor, false); + if (ret) + return ret; + } + + ret = ops->set_threshold(handle, mbox_chan_id, acpm_sensor_id, + thresholds, 2); + if (!ret) { + ret = ops->set_interrupt_enable(handle, mbox_chan_id, + acpm_sensor_id, inten); + } + + if (was_enabled) { + restore_ret = acpm_tmu_op_tz_control(sensor, true); + if (restore_ret) + dev_err(priv->dev, "Failed to restore sensor state: %d\n", + restore_ret); + if (!ret) + ret = restore_ret; + } + + return ret; +} + +static int acpm_tmu_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct acpm_tmu_sensor *sensor = thermal_zone_device_priv(tz); + struct acpm_tmu_priv *priv = sensor->priv; + struct device *dev = priv->dev; + u8 thresholds[2] = {}; + u8 inten = 0; + int ret; + + /* If a valid lower bound exists, set the threshold and enable its interrupt */ + if (low > -INT_MAX) { + thresholds[0] = clamp_val(low / MILLIDEGREE_PER_DEGREE, 0, 255); + inten |= BIT(0); + } + + /* If a valid upper bound exists, set the threshold and enable its interrupt */ + if (high < INT_MAX) { + thresholds[1] = clamp_val(high / MILLIDEGREE_PER_DEGREE, 0, 255); + inten |= BIT(1); + } + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = acpm_tmu_update_thresholds(sensor, thresholds, inten); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static const struct thermal_zone_device_ops acpm_tmu_sensor_ops = { + .get_temp = acpm_tmu_get_temp, + .set_trips = acpm_tmu_set_trips, +}; + +static int acpm_tmu_has_pending_irq(struct acpm_tmu_sensor *sensor, + bool *pending_irq) +{ + struct acpm_tmu_priv *priv = sensor->priv; + unsigned long mask = sensor->group->mask; + int i, ret; + u32 val; + + guard(mutex)(&sensor->lock); + + for_each_set_bit(i, &mask, EXYNOS_TMU_SENSORS_MAX_COUNT) { + ret = regmap_field_read(priv->regmap_fields[i], &val); + if (ret) + return ret; + + if (val) { + *pending_irq = true; + break; + } + } + + return 0; +} + +static irqreturn_t acpm_tmu_thread_fn(int irq, void *id) +{ + struct acpm_tmu_priv *priv = id; + struct acpm_handle *handle = priv->handle; + const struct acpm_tmu_ops *ops = &handle->ops->tmu; + struct device *dev = priv->dev; + bool handled = false; + int i, ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "Failed to resume: %d\n", ret); + return IRQ_NONE; + } + + for (i = 0; i < priv->num_sensors; i++) { + struct acpm_tmu_sensor *sensor = &priv->sensors[i]; + bool pending_irq = false; + + if (!sensor->tzd) + continue; + + ret = acpm_tmu_has_pending_irq(sensor, &pending_irq); + if (ret || !pending_irq) + continue; + + handled = true; + + scoped_guard(mutex, &sensor->lock) { + ret = ops->clear_tz_irq(handle, priv->mbox_chan_id, + sensor->group->id); + if (ret) + dev_err(priv->dev, "Sensor %d: failed to clear IRQ (%d)\n", + i, ret); + } + + thermal_zone_device_update(sensor->tzd, + THERMAL_EVENT_UNSPECIFIED); + } + + pm_runtime_put_autosuspend(dev); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct of_device_id acpm_tmu_match[] = { + { .compatible = "google,gs101-tmu-top" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, acpm_tmu_match); + +static int acpm_tmu_probe(struct platform_device *pdev) +{ + const struct acpm_tmu_driver_data *data = &acpm_tmu_gs101; + struct acpm_handle *acpm_handle; + struct device *dev = &pdev->dev; + struct acpm_tmu_priv *priv; + struct regmap *regmap; + void __iomem *base; + int i, ret; + + acpm_handle = devm_acpm_get_by_phandle(dev); + if (IS_ERR(acpm_handle)) + return dev_err_probe(dev, PTR_ERR(acpm_handle), + "Failed to get ACPM handle\n"); + + priv = devm_kzalloc(dev, + struct_size(priv, sensors, data->num_sensor_groups), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->handle = acpm_handle; + priv->mbox_chan_id = data->mbox_chan_id; + priv->num_sensors = data->num_sensor_groups; + + platform_set_drvdata(pdev, priv); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "Failed to ioremap resource\n"); + + regmap = devm_regmap_init_mmio(dev, base, &gs101_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); + + ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->regmap_fields, + data->reg_fields, REG_INTPEND_COUNT); + if (ret) + return dev_err_probe(dev, ret, + "Unable to map syscon registers\n"); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Failed to get the clock\n"); + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) + return dev_err_probe(dev, priv->irq, "Failed to get irq\n"); + + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to resume device\n"); + + ret = acpm_handle->ops->tmu.init(acpm_handle, priv->mbox_chan_id); + if (ret) { + ret = dev_err_probe(dev, ret, "Failed to init TMU\n"); + goto err_pm_put; + } + + for (i = 0; i < priv->num_sensors; i++) { + struct acpm_tmu_sensor *sensor = &priv->sensors[i]; + + mutex_init(&sensor->lock); + sensor->group = &data->sensor_groups[i]; + sensor->priv = priv; + + sensor->tzd = devm_thermal_of_zone_register(dev, i, sensor, + &acpm_tmu_sensor_ops); + if (IS_ERR(sensor->tzd)) { + ret = PTR_ERR(sensor->tzd); + if (ret == -ENODEV) { + sensor->tzd = NULL; + dev_dbg(dev, "Sensor %d not used in DT, skipping\n", i); + continue; + } + + ret = dev_err_probe(dev, ret, "Failed to register sensor %d\n", i); + goto err_rollback; + } + + mutex_lock(&sensor->lock); + ret = acpm_tmu_op_tz_control(sensor, true); + mutex_unlock(&sensor->lock); + if (ret) { + ret = dev_err_probe(dev, ret, "Failed to enable sensor %d\n", i); + goto err_rollback; + } + + thermal_zone_device_update(sensor->tzd, + THERMAL_EVENT_UNSPECIFIED); + + ret = devm_thermal_add_hwmon_sysfs(dev, sensor->tzd); + if (ret) + dev_warn(dev, "Failed to add hwmon sysfs!\n"); + } + + ret = devm_request_threaded_irq(dev, priv->irq, NULL, + acpm_tmu_thread_fn, IRQF_ONESHOT, + dev_name(dev), priv); + if (ret) { + ret = dev_err_probe(dev, ret, "Failed to request irq\n"); + goto err_rollback; + } + + pm_runtime_put_autosuspend(dev); + + return 0; + +err_rollback: + acpm_tmu_control_rollback(priv, i - 1); +err_pm_put: + pm_runtime_put_sync(dev); + return ret; +} + +static void acpm_tmu_remove(struct platform_device *pdev) +{ + struct acpm_tmu_priv *priv = platform_get_drvdata(pdev); + + /* Stop IRQ first to prevent race with thread_fn */ + disable_irq(priv->irq); + + /* + * Disable autosuspend to force the subsequent pm_runtime_put_sync() + * inside acpm_tmu_control() to synchronously suspend the device + * immediately, preventing clock leaks when the driver is removed. + */ + pm_runtime_dont_use_autosuspend(&pdev->dev); + acpm_tmu_control(priv, false); +} + +static int acpm_tmu_suspend(struct device *dev) +{ + struct acpm_tmu_priv *priv = dev_get_drvdata(dev); + struct acpm_handle *handle = priv->handle; + const struct acpm_tmu_ops *ops = &handle->ops->tmu; + int ret; + + ret = acpm_tmu_control(priv, false); + if (ret) + return ret; + + /* APB clock not required for this specific msg */ + ret = ops->suspend(handle, priv->mbox_chan_id); + if (ret) { + int restore_ret = acpm_tmu_control(priv, true); + + if (restore_ret) + dev_err(dev, "Failed to re-enable TMU after suspend failure: %d\n", + restore_ret); + return ret; + } + + return 0; +} + +static int acpm_tmu_resume(struct device *dev) +{ + struct acpm_tmu_priv *priv = dev_get_drvdata(dev); + struct acpm_handle *handle = priv->handle; + const struct acpm_tmu_ops *ops = &handle->ops->tmu; + int ret; + + /* APB clock not required for this specific msg */ + ret = ops->resume(handle, priv->mbox_chan_id); + if (ret) + return ret; + + return acpm_tmu_control(priv, true); +} + +static int acpm_tmu_runtime_suspend(struct device *dev) +{ + struct acpm_tmu_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int acpm_tmu_runtime_resume(struct device *dev) +{ + struct acpm_tmu_priv *priv = dev_get_drvdata(dev); + + return clk_prepare_enable(priv->clk); +} + +static const struct dev_pm_ops acpm_tmu_pm_ops = { + SYSTEM_SLEEP_PM_OPS(acpm_tmu_suspend, acpm_tmu_resume) + RUNTIME_PM_OPS(acpm_tmu_runtime_suspend, acpm_tmu_runtime_resume, NULL) +}; + +static struct platform_driver acpm_tmu_driver = { + .driver = { + .name = "gs-tmu", + .pm = pm_ptr(&acpm_tmu_pm_ops), + .of_match_table = acpm_tmu_match, + }, + .probe = acpm_tmu_probe, + .remove = acpm_tmu_remove, +}; +module_platform_driver(acpm_tmu_driver); + +MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>"); +MODULE_DESCRIPTION("Samsung Exynos ACPM TMU Driver"); +MODULE_LICENSE("GPL"); -- 2.54.0.1013.g208068f2d8-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 2026-06-02 17:00 ` [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 Tudor Ambarus @ 2026-06-02 17:14 ` sashiko-bot 2026-06-02 19:49 ` Alexey Klimov 1 sibling, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-06-02 17:14 UTC (permalink / raw) To: Tudor Ambarus; +Cc: robh, conor+dt, devicetree Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] Missing Error Rollback in `acpm_tmu_suspend` leaves thermal sensors permanently disabled if system suspend fails. - [High] Clock and power leak during probe error path and system sleep due to autosuspend preventing synchronous runtime suspend. - [Medium] Kconfig dependency allows COMPILE_TEST without EXYNOS_ACPM_PROTOCOL, leading to a linker error. -- commit 72d6aa9ba0b741ec961e0a99b3827f24785a2e52 Author: Tudor Ambarus <tudor.ambarus@linaro.org> thermal: samsung: Add Exynos ACPM TMU driver GS101 Add driver for the Thermal Management Unit (TMU) managed via the Alive Clock and Power Manager (ACPM), found on Samsung Exynos SoCs such as Google GS101 (and Exynos850, autov920, etc.). > diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig > index f4eff5a41a84c..383ae3f56cbb2 100644 > --- a/drivers/thermal/samsung/Kconfig > +++ b/drivers/thermal/samsung/Kconfig [ ... ] > @@ -9,3 +9,22 @@ config EXYNOS_THERMAL > the TMU, reports temperature and handles cooling action if defined. > This driver uses the Exynos core thermal APIs and TMU configuration > data from the supported SoCs. > + > +config EXYNOS_ACPM_THERMAL > + tristate "Exynos ACPM thermal management unit driver" > + depends on THERMAL_OF > + depends on HAS_IOMEM > + depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL) [Severity: Medium] Does this dependency rule permit compilation when the underlying protocol is not built, leading to a linker error? The Kconfig option explicitly allows compilation when COMPILE_TEST is enabled and EXYNOS_ACPM_PROTOCOL is disabled. However, the driver unconditionally calls devm_acpm_get_by_phandle() in acpm_tmu_probe(). Since include/linux/firmware/samsung/exynos-acpm-protocol.h does not provide empty inline stub implementations for these APIs when the protocol is disabled, building the driver in this configuration might result in an unresolved symbol error at link time. > diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c > new file mode 100644 > index 0000000000000..2de34b03b9eab > --- /dev/null > +++ b/drivers/thermal/samsung/acpm-tmu.c [ ... ] > +err_rollback: > + acpm_tmu_control_rollback(priv, i - 1); > +err_pm_put: > + pm_runtime_put_sync(dev); > + return ret; > +} [Severity: High] Will this leak the clock and power during the probe error path? The driver enables runtime PM autosuspend with a 100ms delay. On error paths in acpm_tmu_probe(), pm_runtime_put_sync() is called. Because autosuspend is active, this drops the usage count but only schedules an asynchronous suspend, leaving the device active. When the probe function returns the error, devres cleanup automatically invokes pm_runtime_disable(), which cancels the pending autosuspend timer. The device gets permanently stuck in an active state, the runtime suspend callback is never invoked, and the clock is leaked. [ ... ] > +static int acpm_tmu_suspend(struct device *dev) > +{ > + struct acpm_tmu_priv *priv = dev_get_drvdata(dev); > + struct acpm_handle *handle = priv->handle; > + const struct acpm_tmu_ops *ops = &handle->ops->tmu; > + int ret; > + > + ret = acpm_tmu_control(priv, false); > + if (ret) > + return ret; [Severity: High] Should this roll back the partially disabled thermal sensors on failure? If acpm_tmu_control() fails, the driver immediately returns the error. Because acpm_tmu_control() is designed to continue processing even if an individual sensor fails to disable, a failure return implies that some sensors may have been successfully disabled while others failed. Returning an error aborts the system suspend transition, meaning the PM core assumes the device is still active and will not invoke the resume callback. The successfully disabled sensors are thus left disabled indefinitely, which silently breaks thermal mitigation on an active system. Should we call acpm_tmu_control(priv, true) on failure, similar to the existing rollback logic for ops->suspend()? [Severity: High] Additionally, will this leak the clock during system sleep due to autosuspend? acpm_tmu_control() calls pm_runtime_put_sync(), which schedules an autosuspend. During the subsequent suspend_late phase, the PM core calls pm_runtime_disable(), cancelling the timer. The device remains active, and its clock remains enabled throughout the entire system sleep duration. It looks like acpm_tmu_remove() mitigates this by calling pm_runtime_dont_use_autosuspend() before dropping the reference. Could a similar fix be applied here and in the probe error paths? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260602-acpm-tmu-v7-0-8047dd54b0e9@linaro.org?part=2 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 2026-06-02 17:00 ` [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 Tudor Ambarus 2026-06-02 17:14 ` sashiko-bot @ 2026-06-02 19:49 ` Alexey Klimov 1 sibling, 0 replies; 9+ messages in thread From: Alexey Klimov @ 2026-06-02 19:49 UTC (permalink / raw) To: Tudor Ambarus, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik, Alim Akhtar Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree, linux-hardening, linux-arm-kernel, Krzysztof Kozlowski On Tue Jun 2, 2026 at 6:00 PM BST, Tudor Ambarus wrote: > Add driver for the Thermal Management Unit (TMU) managed via the Alive > Clock and Power Manager (ACPM), found on Samsung Exynos SoCs such as > Google GS101 (and Exynos850, autov920, etc.). > > The TMU on utilizes a hybrid management model shared between the > Application Processor (AP) and the ACPM firmware. The driver maintains > direct memory-mapped access to the TMU interrupt pending registers to > identify thermal events, while delegating functional tasks - such as > sensor initialization, threshold configuration, and temperature > acquisition - to the ACPM firmware via the ACPM IPC protocol. > > Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org> > Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> > --- > drivers/thermal/samsung/Kconfig | 19 ++ > drivers/thermal/samsung/Makefile | 2 + > drivers/thermal/samsung/acpm-tmu.c | 618 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 639 insertions(+) > > diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig > index f4eff5a41a84..383ae3f56cbb 100644 > --- a/drivers/thermal/samsung/Kconfig > +++ b/drivers/thermal/samsung/Kconfig > @@ -9,3 +9,22 @@ config EXYNOS_THERMAL > the TMU, reports temperature and handles cooling action if defined. > This driver uses the Exynos core thermal APIs and TMU configuration > data from the supported SoCs. > + > +config EXYNOS_ACPM_THERMAL > + tristate "Exynos ACPM thermal management unit driver" > + depends on THERMAL_OF > + depends on HAS_IOMEM > + depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL) > + default ARCH_EXYNOS > + help > + Support for the Thermal Management Unit (TMU) on Samsung Exynos SoCs > + (such as Google GS101 and Exynos850). This driver doesn't support Exynos850. There is no initialisation sequence and etc, moreover the next section is also not entirely correct for Exynos850. Not sure why it is mentioned here in such way. (Not even mentioning that ACPM TMU part is not aligned for Exynos850) > + The TMU on these platforms is managed through a hybrid architecture. > + This driver handles direct register access for thermal interrupt status > + monitoring and communicates with the Alive Clock and Power Manager > + (ACPM) firmware via the ACPM IPC protocol for functional sensor control > + and configuration. > + Select this if you want to monitor device temperature and enable > + thermal mitigation on Samsung Exynos ACPM based devices. [..] BR, Alexey ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v7 3/5] MAINTAINERS: Add entry for Samsung Exynos ACPM thermal driver 2026-06-02 17:00 [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 1/5] dt-bindings: thermal: Add " Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 Tudor Ambarus @ 2026-06-02 17:00 ` Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 4/5] arm64: dts: exynos: gs101: Add thermal management unit Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 5/5] arm64: defconfig: enable Exynos ACPM thermal support Tudor Ambarus 4 siblings, 0 replies; 9+ messages in thread From: Tudor Ambarus @ 2026-06-02 17:00 UTC (permalink / raw) To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik, Alim Akhtar Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree, linux-hardening, linux-arm-kernel, Tudor Ambarus, Krzysztof Kozlowski Add a MAINTAINERS entry for the Samsung Exynos ACPM thermal driver. Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e8218c2749b7..6a8521270daf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23661,6 +23661,14 @@ F: drivers/clk/samsung/clk-acpm.c F: drivers/firmware/samsung/exynos-acpm* F: include/linux/firmware/samsung/exynos-acpm-protocol.h +SAMSUNG EXYNOS ACPM THERMAL DRIVER +M: Tudor Ambarus <tudor.ambarus@linaro.org> +L: linux-kernel@vger.kernel.org +L: linux-samsung-soc@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml +F: drivers/thermal/samsung/acpm-tmu.c + SAMSUNG EXYNOS MAILBOX DRIVER M: Tudor Ambarus <tudor.ambarus@linaro.org> L: linux-kernel@vger.kernel.org -- 2.54.0.1013.g208068f2d8-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v7 4/5] arm64: dts: exynos: gs101: Add thermal management unit 2026-06-02 17:00 [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU Tudor Ambarus ` (2 preceding siblings ...) 2026-06-02 17:00 ` [PATCH v7 3/5] MAINTAINERS: Add entry for Samsung Exynos ACPM thermal driver Tudor Ambarus @ 2026-06-02 17:00 ` Tudor Ambarus 2026-06-02 17:22 ` sashiko-bot 2026-06-02 17:00 ` [PATCH v7 5/5] arm64: defconfig: enable Exynos ACPM thermal support Tudor Ambarus 4 siblings, 1 reply; 9+ messages in thread From: Tudor Ambarus @ 2026-06-02 17:00 UTC (permalink / raw) To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik, Alim Akhtar Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree, linux-hardening, linux-arm-kernel, Tudor Ambarus Add the Thermal Management Unit (TMU) support for the Google GS101 SoC. Describe the TMU using a consolidated SoC node that includes memory resources for interrupt identification and a phandle to the ACPM IPC interface for functional control. Define thermal zones for the little, mid, and big CPU clusters, including associated trip points and cooling-device maps to enable thermal mitigation. Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org> --- arch/arm64/boot/dts/exynos/google/gs101-tmu.dtsi | 136 +++++++++++++++++++++++ arch/arm64/boot/dts/exynos/google/gs101.dtsi | 18 +++ 2 files changed, 154 insertions(+) diff --git a/arch/arm64/boot/dts/exynos/google/gs101-tmu.dtsi b/arch/arm64/boot/dts/exynos/google/gs101-tmu.dtsi new file mode 100644 index 000000000000..b27d1a539ec2 --- /dev/null +++ b/arch/arm64/boot/dts/exynos/google/gs101-tmu.dtsi @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Google GS101 TMU configurations device tree source + * + * Copyright 2020 Samsung Electronics Co., Ltd. + * Copyright 2020 Google LLC. + * Copyright 2026 Linaro Ltd. + */ + +#include <dt-bindings/thermal/thermal.h> + +/ { + thermal-zones { + cpucl2-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tmu_top 0>; + + trips { + big_switch_on: big-switch-on { + temperature = <80000>; + hysteresis = <2000>; + type = "passive"; + }; + + big_mitigate: big-mitigate { + temperature = <90000>; + hysteresis = <5000>; + type = "passive"; + }; + + big_hot: big-hot { + temperature = <100000>; + hysteresis = <5000>; + type = "hot"; + }; + + big_critical: big-critical { + temperature = <105000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&big_mitigate>; + cooling-device = <&cpu6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpucl1-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tmu_top 1>; + + trips { + mid_switch_on: mid-switch-on { + temperature = <80000>; + hysteresis = <2000>; + type = "passive"; + }; + + mid_mitigate: mid-mitigate { + temperature = <90000>; + hysteresis = <5000>; + type = "passive"; + }; + + mid_hot: mid-hot { + temperature = <100000>; + hysteresis = <5000>; + type = "hot"; + }; + + mid_critical: mid-critical { + temperature = <105000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&mid_mitigate>; + cooling-device = <&cpu4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpucl0-thermal { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&tmu_top 2>; + + trips { + little_switch_on: little-switch-on { + temperature = <80000>; + hysteresis = <2000>; + type = "passive"; + }; + + little_mitigate: little-mitigate { + temperature = <90000>; + hysteresis = <5000>; + type = "passive"; + }; + + little_hot: little-hot { + temperature = <100000>; + hysteresis = <5000>; + type = "hot"; + }; + + little_critical: little-critical { + temperature = <105000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&little_mitigate>; + cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/exynos/google/gs101.dtsi b/arch/arm64/boot/dts/exynos/google/gs101.dtsi index 86933f22647b..b6866ef99fb3 100644 --- a/arch/arm64/boot/dts/exynos/google/gs101.dtsi +++ b/arch/arm64/boot/dts/exynos/google/gs101.dtsi @@ -74,6 +74,7 @@ cpu0: cpu@0 { compatible = "arm,cortex-a55"; reg = <0x0000>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL0>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&ananke_cpu_sleep>; capacity-dmips-mhz = <250>; @@ -86,6 +87,7 @@ cpu1: cpu@100 { compatible = "arm,cortex-a55"; reg = <0x0100>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL0>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&ananke_cpu_sleep>; capacity-dmips-mhz = <250>; @@ -98,6 +100,7 @@ cpu2: cpu@200 { compatible = "arm,cortex-a55"; reg = <0x0200>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL0>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&ananke_cpu_sleep>; capacity-dmips-mhz = <250>; @@ -110,6 +113,7 @@ cpu3: cpu@300 { compatible = "arm,cortex-a55"; reg = <0x0300>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL0>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&ananke_cpu_sleep>; capacity-dmips-mhz = <250>; @@ -122,6 +126,7 @@ cpu4: cpu@400 { compatible = "arm,cortex-a76"; reg = <0x0400>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL1>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&enyo_cpu_sleep>; capacity-dmips-mhz = <620>; @@ -134,6 +139,7 @@ cpu5: cpu@500 { compatible = "arm,cortex-a76"; reg = <0x0500>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL1>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&enyo_cpu_sleep>; capacity-dmips-mhz = <620>; @@ -146,6 +152,7 @@ cpu6: cpu@600 { compatible = "arm,cortex-x1"; reg = <0x0600>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL2>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&hera_cpu_sleep>; capacity-dmips-mhz = <1024>; @@ -158,6 +165,7 @@ cpu7: cpu@700 { compatible = "arm,cortex-x1"; reg = <0x0700>; clocks = <&acpm_ipc GS101_CLK_ACPM_DVFS_CPUCL2>; + #cooling-cells = <2>; enable-method = "psci"; cpu-idle-states = <&hera_cpu_sleep>; capacity-dmips-mhz = <1024>; @@ -639,6 +647,15 @@ watchdog_cl1: watchdog@10070000 { status = "disabled"; }; + tmu_top: thermal-sensor@100a0000 { + compatible = "google,gs101-tmu-top"; + reg = <0x100a0000 0x800>; + clocks = <&cmu_misc CLK_GOUT_MISC_TMU_TOP_PCLK>; + interrupts = <GIC_SPI 769 IRQ_TYPE_LEVEL_HIGH 0>; + samsung,acpm-ipc = <&acpm_ipc>; + #thermal-sensor-cells = <1>; + }; + trng: rng@10141400 { compatible = "google,gs101-trng", "samsung,exynos850-trng"; @@ -1862,3 +1879,4 @@ timer { }; #include "gs101-pinctrl.dtsi" +#include "gs101-tmu.dtsi" -- 2.54.0.1013.g208068f2d8-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v7 4/5] arm64: dts: exynos: gs101: Add thermal management unit 2026-06-02 17:00 ` [PATCH v7 4/5] arm64: dts: exynos: gs101: Add thermal management unit Tudor Ambarus @ 2026-06-02 17:22 ` sashiko-bot 0 siblings, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-06-02 17:22 UTC (permalink / raw) To: Tudor Ambarus; +Cc: conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] The TMU APB clock (priv->clk) is left enabled during system suspend, causing a power leak. -- commit 45da7d1dd2f371f5e33444f5d1be0481464a464a Author: Tudor Ambarus <tudor.ambarus@linaro.org> arm64: dts: exynos: gs101: Add thermal management unit This commit adds Thermal Management Unit (TMU) support for the Google GS101 SoC. It describes the TMU using a consolidated SoC node that includes memory resources and a phandle to the ACPM IPC interface. It also defines thermal zones for the CPU clusters to enable thermal mitigation. > diff --git a/arch/arm64/boot/dts/exynos/google/gs101.dtsi b/arch/arm64/boot/dts/exynos/google/gs101.dtsi > index 86933f22647b7..b6866ef99fb34 100644 > --- a/arch/arm64/boot/dts/exynos/google/gs101.dtsi > +++ b/arch/arm64/boot/dts/exynos/google/gs101.dtsi [ ... ] > @@ -639,6 +647,15 @@ watchdog_cl1: watchdog@10070000 { > status = "disabled"; > }; > > + tmu_top: thermal-sensor@100a0000 { > + compatible = "google,gs101-tmu-top"; > + reg = <0x100a0000 0x800>; > + clocks = <&cmu_misc CLK_GOUT_MISC_TMU_TOP_PCLK>; > + interrupts = <GIC_SPI 769 IRQ_TYPE_LEVEL_HIGH 0>; > + samsung,acpm-ipc = <&acpm_ipc>; > + #thermal-sensor-cells = <1>; > + }; [Severity: High] Does enabling this device tree node lead to a clock leak during system suspend in the corresponding driver? Looking at acpm_tmu_suspend() in drivers/thermal/samsung/acpm-tmu.c: drivers/thermal/samsung/acpm-tmu.c:acpm_tmu_suspend() { ... ret = acpm_tmu_control(priv, false); ... } During system suspend, the core PM subsystem calls pm_runtime_get_noresume() in the device_prepare() phase, which artificially increments the runtime PM usage count to prevent runtime suspend transitions. When acpm_tmu_suspend() is invoked, it calls acpm_tmu_control(), which temporarily increments and decrements the usage count via pm_runtime_resume_and_get() and pm_runtime_put_sync(). Because the core PM still holds its reference, the usage count never drops to 0. Consequently, acpm_tmu_runtime_suspend() is never invoked, meaning clk_disable_unprepare() is skipped. The peripheral clock remains unconditionally enabled for the entire duration of the system sleep, leading to battery drain and preventing deep sleep states for the parent clock and power domain. The driver author intended the clock to be off here (as noted by the comment 'APB clock not required for this specific msg'), but missed the core PM refcount behavior. Should the driver be modified to ensure the clock is disabled or synced via pm_runtime_force_suspend() instead? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260602-acpm-tmu-v7-0-8047dd54b0e9@linaro.org?part=4 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v7 5/5] arm64: defconfig: enable Exynos ACPM thermal support 2026-06-02 17:00 [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU Tudor Ambarus ` (3 preceding siblings ...) 2026-06-02 17:00 ` [PATCH v7 4/5] arm64: dts: exynos: gs101: Add thermal management unit Tudor Ambarus @ 2026-06-02 17:00 ` Tudor Ambarus 4 siblings, 0 replies; 9+ messages in thread From: Tudor Ambarus @ 2026-06-02 17:00 UTC (permalink / raw) To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik, Alim Akhtar Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree, linux-hardening, linux-arm-kernel, Tudor Ambarus Enable the Exynos ACPM thermal driver (CONFIG_EXYNOS_ACPM_THERMAL) to allow temperature monitoring and thermal management on Samsung Exynos SoCs (like Google GS101), used on pixel phones. Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org> --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index d905a0777f93..3fe76a4c2633 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -793,6 +793,7 @@ CONFIG_BCM2711_THERMAL=m CONFIG_BCM2835_THERMAL=m CONFIG_BRCMSTB_THERMAL=m CONFIG_EXYNOS_THERMAL=y +CONFIG_EXYNOS_ACPM_THERMAL=m CONFIG_TEGRA_SOCTHERM=m CONFIG_TEGRA_BPMP_THERMAL=m CONFIG_GENERIC_ADC_THERMAL=m -- 2.54.0.1013.g208068f2d8-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-06-02 19:49 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-02 17:00 [PATCH v7 0/5] thermal: samsung: Add support for Google GS101 TMU Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 1/5] dt-bindings: thermal: Add " Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101 Tudor Ambarus 2026-06-02 17:14 ` sashiko-bot 2026-06-02 19:49 ` Alexey Klimov 2026-06-02 17:00 ` [PATCH v7 3/5] MAINTAINERS: Add entry for Samsung Exynos ACPM thermal driver Tudor Ambarus 2026-06-02 17:00 ` [PATCH v7 4/5] arm64: dts: exynos: gs101: Add thermal management unit Tudor Ambarus 2026-06-02 17:22 ` sashiko-bot 2026-06-02 17:00 ` [PATCH v7 5/5] arm64: defconfig: enable Exynos ACPM thermal support Tudor Ambarus
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox