From ba8a4e072a3a6b9affdb3d231975f3a42252a6fa Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 4 May 2026 16:12:36 +0800 Subject: dt-bindings: thermal: qcom-tsens: Document Nord Temperature Sensor Document Temperature Sensor (TSENS) on Qualcomm Nord SoC. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Signed-off-by: Daniel Lezcano Reviewed-by: Pankaj Patil Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260504081236.825755-1-shengchao.guo@oss.qualcomm.com --- Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml index 7d34ba00e684..e65ebc6f1698 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml @@ -58,6 +58,7 @@ properties: - qcom,glymur-tsens - qcom,kaanapali-tsens - qcom,milos-tsens + - qcom,nord-tsens - qcom,msm8953-tsens - qcom,msm8996-tsens - qcom,msm8998-tsens -- cgit v1.2.3 From 4ab6e05dc2f2b4ea3d8ffc0e0edfb8a5bcd2957f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 24 Apr 2026 18:00:18 +0200 Subject: thermal/drivers/tegra/soctherm: Use devm_add_action_or_reset() for clock disable Replace the manual error handling paths disabling the clocks with devm_add_action_or_reset(). This ensures the clocks are properly disabled on probe failure and driver removal, while simplifying the code by removing the explicit error paths. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20260424160019.41710-1-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/tegra/soctherm.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 5d26b52beaba..40c3715e84c5 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1499,6 +1499,13 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable) return 0; } +static void soctherm_clk_disable(void *data) +{ + struct platform_device *pdev = data; + + soctherm_clk_enable(pdev, false); +} + static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, unsigned long *max_state) { @@ -2175,6 +2182,10 @@ static int tegra_soctherm_probe(struct platform_device *pdev) if (err) return err; + err = devm_add_action_or_reset(&pdev->dev, soctherm_clk_disable, pdev); + if (err) + return err; + soctherm_thermtrips_parse(pdev); soctherm_init_hw_throt_cdev(pdev); @@ -2184,10 +2195,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) for (i = 0; i < soc->num_ttgs; ++i) { struct tegra_thermctl_zone *zone = devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); - if (!zone) { - err = -ENOMEM; - goto disable_clocks; - } + if (!zone) + return -ENOMEM; zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; zone->dev = &pdev->dev; @@ -2201,7 +2210,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) err = PTR_ERR(z); dev_err(&pdev->dev, "failed to register sensor: %d\n", err); - goto disable_clocks; + return err; } zone->tz = z; @@ -2210,7 +2219,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) /* Configure hw trip points */ err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); if (err) - goto disable_clocks; + return err; } err = soctherm_interrupts_init(pdev, tegra); @@ -2218,11 +2227,6 @@ static int tegra_soctherm_probe(struct platform_device *pdev) soctherm_debug_init(pdev); return 0; - -disable_clocks: - soctherm_clk_enable(pdev, false); - - return err; } static void tegra_soctherm_remove(struct platform_device *pdev) @@ -2230,8 +2234,6 @@ static void tegra_soctherm_remove(struct platform_device *pdev) struct tegra_soctherm *tegra = platform_get_drvdata(pdev); debugfs_remove_recursive(tegra->debugfs_dir); - - soctherm_clk_enable(pdev, false); } static int __maybe_unused soctherm_suspend(struct device *dev) -- cgit v1.2.3 From ee126267bc04bfb03816ae9d71ca24c5bf99e739 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 24 Apr 2026 18:00:19 +0200 Subject: thermal/drivers/tegra/soctherma: Switch to devm cooling device registration Use devm_thermal_of_cooling_device_register() to simplify resource management and avoid manual cleanup in error paths. As a side effect this change has the benefit of solving an existing issue. Before, the function tegra_soctherm_remove() only called debugfs_remove_recursive() and never called thermal_cooling_device_unregister() for any of the cooling devices registered here. After the driver removal, the thermal framework's cdev list would still hold references to thermal_cooling_device objects whose devdata pointer (ts) pointed to memory already freed by the platform device's devm cleanup. With this change, the cooling device is unregistered when the driver is removed, thus fixing the issue above. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20260424160019.41710-2-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/tegra/soctherm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 40c3715e84c5..6a56638c98f1 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1707,9 +1707,9 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) stc->init = true; } else { - tcd = thermal_of_cooling_device_register(np_stcc, - (char *)name, ts, - &throt_cooling_ops); + tcd = devm_thermal_of_cooling_device_register(dev, np_stcc, + (char *)name, ts, + &throt_cooling_ops); if (IS_ERR_OR_NULL(tcd)) { dev_err(dev, "throttle-cfg: %s: failed to register cooling device\n", -- cgit v1.2.3 From 31da1d4451c88b63779bc3f855ae15f718c0e539 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 29 Apr 2026 18:14:14 +0200 Subject: thermal/core: Use devm_add_action_or_reset() when registering a cooling device Use devm_add_action_or_reset() which does the replaced code. It results in a simpler and more concise code. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260429161430.3802970-2-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/thermal_core.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 7d7ce855ae88..db01361569d7 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1140,10 +1140,11 @@ thermal_of_cooling_device_register(struct device_node *np, } EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); -static void thermal_cooling_device_release(struct device *dev, void *res) +static void thermal_cooling_device_release(void *data) { - thermal_cooling_device_unregister( - *(struct thermal_cooling_device **)res); + struct thermal_cooling_device *cdev = data; + + thermal_cooling_device_unregister(cdev); } /** @@ -1169,23 +1170,18 @@ devm_thermal_of_cooling_device_register(struct device *dev, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { - struct thermal_cooling_device **ptr, *tcd; - - ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct thermal_cooling_device *cdev; + int ret; - tcd = __thermal_cooling_device_register(np, type, devdata, ops); - if (IS_ERR(tcd)) { - devres_free(ptr); - return tcd; - } + cdev = __thermal_cooling_device_register(np, type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; - *ptr = tcd; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, thermal_cooling_device_release, cdev); + if (ret) + return ERR_PTR(ret); - return tcd; + return cdev; } EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); -- cgit v1.2.3 From b1c8ccdbd4e92db05548bd3ad2a866bacec07490 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Fri, 24 Apr 2026 17:45:09 +0200 Subject: dt-bindings: thermal: amlogic: Add support for T7 Add the amlogic,t7-thermal compatible for the Amlogic T7 thermal sensor. Unlike existing variants which use a phandle to the ao-secure syscon, the T7 relies on a secure monitor interface described by a phandle and a sensor index argument. The T7 integrates multiple thermal sensors, all accessed through the same SMC call. The sensor index argument is required to identify which sensor's calibration data the secure monitor should return, as a single SM_THERMAL_CALIB_READ command serves all of them. Introduce the amlogic,secure-monitor property as a phandle-array and make amlogic,ao-secure or amlogic,secure-monitor conditionally required depending on the compatible. Signed-off-by: Ronald Claveau Signed-off-by: Daniel Lezcano Acked-by: Conor Dooley Link: https://patch.msgid.link/20260424-add-thermal-t7-vim4-v5-1-9040ca36afe2@aliel.fr --- .../bindings/thermal/amlogic,thermal.yaml | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml index 70b273271754..e28612510d67 100644 --- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml @@ -21,7 +21,9 @@ properties: - amlogic,g12a-cpu-thermal - amlogic,g12a-ddr-thermal - const: amlogic,g12a-thermal - - const: amlogic,a1-cpu-thermal + - enum: + - amlogic,a1-cpu-thermal + - amlogic,t7-thermal reg: maxItems: 1 @@ -42,12 +44,34 @@ properties: '#thermal-sensor-cells': const: 0 + amlogic,secure-monitor: + description: phandle to the secure monitor + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to the secure monitor + - description: sensor index to get specific calibration data + required: - compatible - reg - interrupts - clocks - - amlogic,ao-secure + +allOf: + - if: + properties: + compatible: + contains: + enum: + - amlogic,a1-cpu-thermal + - amlogic,g12a-thermal + then: + required: + - amlogic,ao-secure + else: + required: + - amlogic,secure-monitor unevaluatedProperties: false @@ -62,4 +86,13 @@ examples: #thermal-sensor-cells = <0>; amlogic,ao-secure = <&sec_AO>; }; + - | + temperature-sensor@20000 { + compatible = "amlogic,t7-thermal"; + reg = <0x0 0x20000 0x0 0x50>; + interrupts = ; + clocks = <&clkc_periphs CLKID_TS>; + #thermal-sensor-cells = <0>; + amlogic,secure-monitor = <&sm 1>; + }; ... -- cgit v1.2.3 From d0bf38194bc908468e06c0ae7fba7281046c12d8 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Fri, 24 Apr 2026 17:45:10 +0200 Subject: firmware: meson: sm: Thermal calibration read via secure monitor Add SM_THERMAL_CALIB_READ to the secure monitor command enum and introduce meson_sm_get_thermal_calib() to allow drivers to retrieve thermal sensor calibration data through the firmware interface. Signed-off-by: Ronald Claveau Signed-off-by: Daniel Lezcano Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260424-add-thermal-t7-vim4-v5-2-9040ca36afe2@aliel.fr --- include/linux/firmware/meson/meson_sm.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h index 8eaf8922ab02..3ebc2bd9a976 100644 --- a/include/linux/firmware/meson/meson_sm.h +++ b/include/linux/firmware/meson/meson_sm.h @@ -12,6 +12,7 @@ enum { SM_EFUSE_WRITE, SM_EFUSE_USER_MAX, SM_GET_CHIP_ID, + SM_THERMAL_CALIB_READ, SM_A1_PWRC_SET, SM_A1_PWRC_GET, }; @@ -27,5 +28,7 @@ int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, unsigned int bsize, unsigned int cmd_index, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); struct meson_sm_firmware *meson_sm_get(struct device_node *firmware_node); +int meson_sm_get_thermal_calib(struct meson_sm_firmware *fw, u32 *trim_info, + u32 tsensor_id); #endif /* _MESON_SM_FW_H_ */ -- cgit v1.2.3 From 1c5cb7391ef5f869ef333c04d6422c0e0859a870 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Fri, 24 Apr 2026 17:45:11 +0200 Subject: firmware: meson: sm: Add thermal calibration SMC call Add SM_THERMAL_CALIB_READ at SMC ID 0x82000047 in the command table and implement meson_sm_get_thermal_calib(), which forwards the tsensor_id argument to the secure monitor and returns the calibration data. Also realign the CMD() column to improve readability. Signed-off-by: Ronald Claveau Signed-off-by: Daniel Lezcano [ dlezcano: Fixed kernel-doc format warning ] Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260424-add-thermal-t7-vim4-v5-3-9040ca36afe2@aliel.fr --- drivers/firmware/meson/meson_sm.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 3ab67aaa9e5d..ab9751a59b55 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -41,12 +41,13 @@ static const struct meson_sm_chip gxbb_chip = { .cmd_shmem_in_base = 0x82000020, .cmd_shmem_out_base = 0x82000021, .cmd = { - CMD(SM_EFUSE_READ, 0x82000030), - CMD(SM_EFUSE_WRITE, 0x82000031), + CMD(SM_EFUSE_READ, 0x82000030), + CMD(SM_EFUSE_WRITE, 0x82000031), CMD(SM_EFUSE_USER_MAX, 0x82000033), - CMD(SM_GET_CHIP_ID, 0x82000044), - CMD(SM_A1_PWRC_SET, 0x82000093), - CMD(SM_A1_PWRC_GET, 0x82000095), + CMD(SM_GET_CHIP_ID, 0x82000044), + CMD(SM_THERMAL_CALIB_READ, 0x82000047), + CMD(SM_A1_PWRC_SET, 0x82000093), + CMD(SM_A1_PWRC_GET, 0x82000095), { /* sentinel */ }, }, }; @@ -245,6 +246,23 @@ struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node) } EXPORT_SYMBOL_GPL(meson_sm_get); +/** + * meson_sm_get_thermal_calib - Read thermal sensor calibration data. + * @fw: Pointer to secure-monitor firmware. + * @trim_info: Pointer to store the returned calibration data. + * @tsensor_id: Sensor index to identify which sensor's calibration data + * to retrieve + * + * Return: 0 on success, negative error code on failure. + */ +int meson_sm_get_thermal_calib(struct meson_sm_firmware *fw, u32 *trim_info, + u32 tsensor_id) +{ + return meson_sm_call(fw, SM_THERMAL_CALIB_READ, trim_info, tsensor_id, + 0, 0, 0, 0); +} +EXPORT_SYMBOL_GPL(meson_sm_get_thermal_calib); + #define SM_CHIP_ID_LENGTH 119 #define SM_CHIP_ID_OFFSET 4 #define SM_CHIP_ID_SIZE 12 -- cgit v1.2.3 From 5f1e4f65d3002a814c690434f1dd37facc1df6ca Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Tue, 2 Jun 2026 16:19:20 +0200 Subject: thermal/drivers/amlogic: Add missing dependency on MESON_SM The amlogic thermal driver calls meson_sm_get() and meson_sm_get_thermal_calib() which are exported by the meson_sm driver. Without CONFIG_MESON_SM enabled, the build fails with undefined references to these symbols. Add a proper Kconfig dependency on MESON_SM instead of relying on stub functions, which makes the dependency explicit and prevents invalid configurations. Closes: https://lore.kernel.org/oe-kbuild-all/202605291530.en7aGn7w-lkp@intel.com/ Reported-by: Mark Brown Reported-by: kernel test robot Signed-off-by: Ronald Claveau Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/oe-kbuild-all/202605291530.en7aGn7w-lkp@intel.com/ Link: https://patch.msgid.link/20260602-fix-missing-meson_sm-symbol-v3-1-6f7f69cd7d6c@aliel.fr --- drivers/thermal/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index b10080d61860..40a376cf9936 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -436,6 +436,7 @@ config AMLOGIC_THERMAL tristate "Amlogic Thermal Support" default ARCH_MESON depends on OF && ARCH_MESON + depends on MESON_SM help If you say yes here you get support for Amlogic Thermal for G12 SoC Family. -- cgit v1.2.3 From 18d65de8157c93666b2d42f1d46330ee4cb030b7 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Fri, 24 Apr 2026 17:45:12 +0200 Subject: thermal/drivers/amlogic: Add support for secure monitor calibration readout Some SoCs (e.g. T7) expose thermal calibration data through the secure monitor rather than a directly accessible eFuse register. Add a use_sm flag to amlogic_thermal_data to select this path, and retrieve the firmware handle and tsensor_id from the "amlogic,secure-monitor" DT phandle with one fixed argument. Also introduce the amlogic,t7-thermal compatible using this new path. While refactoring, fix a pre-existing bug where amlogic_thermal_initialize() was called after devm_thermal_of_zone_register(), causing the thermal framework to read an uninitialized trim_info on zone registration. Signed-off-by: Ronald Claveau Signed-off-by: Daniel Lezcano Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260424-add-thermal-t7-vim4-v5-4-9040ca36afe2@aliel.fr --- drivers/thermal/amlogic_thermal.c | 112 ++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 30 deletions(-) diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index 5448d772db12..a0b530624b60 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "thermal_hwmon.h" @@ -84,12 +85,14 @@ struct amlogic_thermal_soc_calib_data { * @u_efuse_off: register offset to read fused calibration value * @calibration_parameters: calibration parameters structure pointer * @regmap_config: regmap config for the device + * @use_sm: read data from secure monitor instead of efuse * This structure is required for configuration of amlogic thermal driver. */ struct amlogic_thermal_data { int u_efuse_off; const struct amlogic_thermal_soc_calib_data *calibration_parameters; const struct regmap_config *regmap_config; + bool use_sm; }; struct amlogic_thermal { @@ -100,6 +103,8 @@ struct amlogic_thermal { struct clk *clk; struct thermal_zone_device *tzd; u32 trim_info; + struct meson_sm_firmware *sm_fw; + u32 tsensor_id; }; /* @@ -133,26 +138,6 @@ static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata, return temp; } -static int amlogic_thermal_initialize(struct amlogic_thermal *pdata) -{ - int ret = 0; - int ver; - - regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off, - &pdata->trim_info); - - ver = TSENSOR_TRIM_VERSION(pdata->trim_info); - - if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) { - ret = -EINVAL; - dev_err(&pdata->pdev->dev, - "tsensor thermal calibration not supported: 0x%x!\n", - ver); - } - - return ret; -} - static int amlogic_thermal_enable(struct amlogic_thermal *data) { int ret; @@ -190,6 +175,67 @@ static int amlogic_thermal_get_temp(struct thermal_zone_device *tz, int *temp) return 0; } +static int amlogic_thermal_probe_sm(struct platform_device *pdev, + struct amlogic_thermal *pdata) +{ + struct device *dev = &pdev->dev; + struct of_phandle_args ph_args; + int ret; + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "amlogic,secure-monitor", + 1, 0, &ph_args); + if (ret) + return ret; + + if (!ph_args.np) { + dev_err(dev, "Failed to parse secure monitor phandle\n"); + return -ENODEV; + } + + pdata->sm_fw = meson_sm_get(ph_args.np); + of_node_put(ph_args.np); + if (!pdata->sm_fw) { + dev_err(dev, "Failed to get secure monitor firmware\n"); + return -EPROBE_DEFER; + } + + pdata->tsensor_id = ph_args.args[0]; + + return meson_sm_get_thermal_calib(pdata->sm_fw, + &pdata->trim_info, + pdata->tsensor_id); +} + +static int amlogic_thermal_probe_syscon(struct platform_device *pdev, + struct amlogic_thermal *pdata) +{ + struct device *dev = &pdev->dev; + int ver; + + pdata->sec_ao_map = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,ao-secure"); + if (IS_ERR(pdata->sec_ao_map)) { + dev_err(dev, "syscon regmap lookup failed.\n"); + return PTR_ERR(pdata->sec_ao_map); + } + + regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off, + &pdata->trim_info); + + ver = TSENSOR_TRIM_VERSION(pdata->trim_info); + + if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) { + dev_err(&pdata->pdev->dev, + "tsensor thermal calibration not supported: 0x%x!\n", + ver); + return -EINVAL; + } + + return 0; +} + static const struct thermal_zone_device_ops amlogic_thermal_ops = { .get_temp = amlogic_thermal_get_temp, }; @@ -226,6 +272,12 @@ static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param = { .regmap_config = &amlogic_thermal_regmap_config_g12a, }; +static const struct amlogic_thermal_data amlogic_thermal_t7_param = { + .use_sm = true, + .calibration_parameters = &amlogic_thermal_g12a, + .regmap_config = &amlogic_thermal_regmap_config_g12a, +}; + static const struct of_device_id of_amlogic_thermal_match[] = { { .compatible = "amlogic,g12a-ddr-thermal", @@ -239,6 +291,10 @@ static const struct of_device_id of_amlogic_thermal_match[] = { .compatible = "amlogic,a1-cpu-thermal", .data = &amlogic_thermal_a1_cpu_param, }, + { + .compatible = "amlogic,t7-thermal", + .data = &amlogic_thermal_t7_param, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match); @@ -271,12 +327,12 @@ static int amlogic_thermal_probe(struct platform_device *pdev) if (IS_ERR(pdata->clk)) return dev_err_probe(dev, PTR_ERR(pdata->clk), "failed to get clock\n"); - pdata->sec_ao_map = syscon_regmap_lookup_by_phandle - (pdev->dev.of_node, "amlogic,ao-secure"); - if (IS_ERR(pdata->sec_ao_map)) { - dev_err(dev, "syscon regmap lookup failed.\n"); - return PTR_ERR(pdata->sec_ao_map); - } + if (pdata->data->use_sm) + ret = amlogic_thermal_probe_sm(pdev, pdata); + else + ret = amlogic_thermal_probe_syscon(pdev, pdata); + if (ret) + return ret; pdata->tzd = devm_thermal_of_zone_register(&pdev->dev, 0, @@ -290,10 +346,6 @@ static int amlogic_thermal_probe(struct platform_device *pdev) devm_thermal_add_hwmon_sysfs(&pdev->dev, pdata->tzd); - ret = amlogic_thermal_initialize(pdata); - if (ret) - return ret; - ret = amlogic_thermal_enable(pdata); return ret; -- cgit v1.2.3 From 4bb840492cedf89d5bed72ede6e0f8e848141977 Mon Sep 17 00:00:00 2001 From: Jinseok Kim Date: Sun, 17 May 2026 00:23:16 +0900 Subject: thermal/drivers/qcom: Fix typo in comment Fix a typo in the struct tsens_irq_data comment. Replace "uppper" with "upper". Signed-off-by: Jinseok Kim Signed-off-by: Daniel Lezcano Acked-by: Amit Kucheria Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260516152324.1863-1-always.starving0@gmail.com --- drivers/thermal/qcom/tsens.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index a2422ebee816..5e19c7588e03 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -27,7 +27,7 @@ * @up_viol: upper threshold violated * @up_thresh: upper threshold temperature value * @up_irq_mask: mask register for upper threshold irqs - * @up_irq_clear: clear register for uppper threshold irqs + * @up_irq_clear: clear register for upper threshold irqs * @low_viol: lower threshold violated * @low_thresh: lower threshold temperature value * @low_irq_mask: mask register for lower threshold irqs -- cgit v1.2.3 From fc4ad576cb836b0538d214b789adf7e89287fb78 Mon Sep 17 00:00:00 2001 From: Gaurav Kohli Date: Wed, 13 May 2026 13:00:58 +0530 Subject: dt-bindings: thermal: qcom-tsens: Document the Shikra Temperature Sensor Document the Temperature Sensor (TSENS) on the Shikra SoC. Signed-off-by: Gaurav Kohli Signed-off-by: Daniel Lezcano Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260513-tsens_binding-v1-1-1780c6a6caf2@oss.qualcomm.com --- Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml index e65ebc6f1698..fd6a67ac28f6 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml @@ -75,6 +75,7 @@ properties: - qcom,sdm630-tsens - qcom,sdm670-tsens - qcom,sdm845-tsens + - qcom,shikra-tsens - qcom,sm6115-tsens - qcom,sm6350-tsens - qcom,sm6375-tsens -- cgit v1.2.3 From dc10bb3cd2e9b44dbb7602e4f5bb96cdd9d62e25 Mon Sep 17 00:00:00 2001 From: Dipa Ramesh Mantre Date: Tue, 12 May 2026 16:43:36 +0530 Subject: dt-bindings: thermal: qcom-tsens: Document the Hawi Temperature Sensor Document the Temperature Sensor (TSENS) on the Qualcomm Hawi SoC. Signed-off-by: Dipa Ramesh Mantre Signed-off-by: Daniel Lezcano Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260512-dtbinding-hawi-v1-1-96149d06cccf@oss.qualcomm.com --- Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml index fd6a67ac28f6..f0efaa8349ee 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml @@ -56,6 +56,7 @@ properties: - enum: - qcom,eliza-tsens - qcom,glymur-tsens + - qcom,hawi-tsens - qcom,kaanapali-tsens - qcom,milos-tsens - qcom,nord-tsens -- cgit v1.2.3 From e28ef2f3ccea276436bd0f30c93f99e764ba492b Mon Sep 17 00:00:00 2001 From: Priyansh Jain Date: Thu, 14 May 2026 17:06:43 +0530 Subject: thermal/drivers/qcom/tsens: Atomic temperature read with hardware-guided retries The existing TSENS temperature read logic polls the valid bit and then reads the temperature register. When temperature reads are triggered at very short intervals, this can race with hardware updates and allow the temperature field to be read while it is still being updated. In this case, the valid bit may already be asserted even though the temperature value is transitioning, resulting in an incorrect reading. Hardware programming guidelines require the temperature value and the valid bit to be sampled atomically in the same read transaction. A reading is considered valid only if the valid bit is observed set in that same sample. The guidelines further specify that software should attempt the temperature read up to three times to account for transient update windows. If none of the attempts yields a valid sample, a stable fallback value must be returned: if the first and second samples match, the second value is returned;otherwise, if the second and third samples match, the third value is returned;if neither pair matches, -EAGAIN is returned. Update the TSENS sensor read logic to implement atomic sampling along with the recommended retry-and-compare fallback behavior. This removes the race window and ensures deterministic temperature values in accordance with hardware requirements. Signed-off-by: Priyansh Jain Signed-off-by: Daniel Lezcano Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260514113643.1954111-1-priyansh.jain@oss.qualcomm.com --- drivers/thermal/qcom/tsens.c | 111 ++++++++++++++++++++++++++++++------------- drivers/thermal/qcom/tsens.h | 1 + 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index 5e19c7588e03..cf7fc0d57a54 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -316,9 +316,66 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) } /** - * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. + * tsens_read_temp - Retrieve temperature readings from the hardware. * @s: Pointer to sensor struct * @field: Index into regmap_field array pointing to temperature data + * @temp: temperature in deciCelsius to be read from hardware + * + * This function handles temperature returned in ADC code or deciCelsius + * depending on IP version. + * + * Return: 0 on success, a negative errno will be returned in error cases + */ +static int tsens_read_temp(const struct tsens_sensor *s, int field, int *temp) +{ + struct tsens_priv *priv = s->priv; + int temp_val[MAX_READ_RETRY] = {0}; + u32 status; + int ret; + u32 last_temp_mask = GENMASK(priv->fields[LAST_TEMP_0].msb, + priv->fields[LAST_TEMP_0].lsb); + u32 valid_bit = priv->rf[VALID_0] ? BIT(priv->fields[VALID_0].lsb) : 0; + + for (int i = 0; i < MAX_READ_RETRY; i++) { + ret = regmap_read(priv->tm_map, priv->fields[field].reg, &status); + if (ret) + return ret; + + /* VER_0 doesn't have a VALID bit */ + if (!valid_bit) { + *temp = status & last_temp_mask; + return 0; + } + + temp_val[i] = status & last_temp_mask; + + if (status & valid_bit) { + *temp = temp_val[i]; + return 0; + } + } + + /* + * As per the HW guidelines, if none of the attempts observe a + * valid sample, a stable fallback value must be returned. If the + * first and second samples match, the second value is returned; + * otherwise, if the second and third samples match, the third + * value is returned. + */ + if (temp_val[0] == temp_val[1]) + *temp = temp_val[1]; + else if (temp_val[1] == temp_val[2]) + *temp = temp_val[2]; + else + return -EAGAIN; + + return 0; +} + +/** + * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. + * @s: Pointer to sensor struct + * @temp: temperature in milliCelsius to be read from hardware * * This function handles temperature returned in ADC code or deciCelsius * depending on IP version. @@ -326,20 +383,14 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) * Return: Temperature in milliCelsius on success, a negative errno will * be returned in error cases */ -static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) +static int tsens_hw_to_mC(const struct tsens_sensor *s, int temp) { struct tsens_priv *priv = s->priv; u32 resolution; - u32 temp = 0; - int ret; resolution = priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb; - ret = regmap_field_read(priv->rf[field], &temp); - if (ret) - return ret; - /* Convert temperature from ADC code to milliCelsius */ if (priv->feat->adc) return code_to_degc(temp, s) * 1000; @@ -514,8 +565,10 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, &d->crit_irq_mask); if (ret) return ret; - - d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); + ret = regmap_field_read(priv->rf[CRIT_THRESH_0 + hw_id], &d->crit_thresh); + if (ret) + return ret; + d->crit_thresh = tsens_hw_to_mC(s, d->crit_thresh); } else { /* No mask register on older TSENS */ d->up_irq_mask = 0; @@ -525,8 +578,16 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, d->crit_thresh = 0; } - d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); - d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); + ret = regmap_field_read(priv->rf[UP_THRESH_0 + hw_id], &d->up_thresh); + if (ret) + return ret; + + d->up_thresh = tsens_hw_to_mC(s, d->up_thresh); + ret = regmap_field_read(priv->rf[LOW_THRESH_0 + hw_id], &d->low_thresh); + if (ret) + return ret; + + d->low_thresh = tsens_hw_to_mC(s, d->low_thresh); dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", hw_id, __func__, @@ -750,33 +811,15 @@ static void tsens_disable_irq(struct tsens_priv *priv) int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) { - struct tsens_priv *priv = s->priv; int hw_id = s->hw_id; u32 temp_idx = LAST_TEMP_0 + hw_id; - u32 valid_idx = VALID_0 + hw_id; - u32 valid; int ret; - /* VER_0 doesn't have VALID bit */ - if (tsens_version(priv) == VER_0) - goto get_temp; - - /* Valid bit is 0 for 6 AHB clock cycles. - * At 19.2MHz, 1 AHB clock is ~60ns. - * We should enter this loop very, very rarely. - * Wait 1 us since it's the min of poll_timeout macro. - * Old value was 400 ns. - */ - ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid, - valid, 1, 20 * USEC_PER_MSEC); - if (ret) - return ret; - -get_temp: - /* Valid bit is set, OK to read the temperature */ - *temp = tsens_hw_to_mC(s, temp_idx); + ret = tsens_read_temp(s, temp_idx, temp); + if (!ret) + *temp = tsens_hw_to_mC(s, *temp); - return 0; + return ret; } int get_temp_common(const struct tsens_sensor *s, int *temp) diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 2a7afa4c899b..ab57ad88c3f7 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -21,6 +21,7 @@ #define THRESHOLD_MIN_ADC_CODE 0x0 #define MAX_SENSORS 16 +#define MAX_READ_RETRY 3 #include #include -- cgit v1.2.3 From 375a4a81968cc8bb56685c99bf1153bc70eb3b29 Mon Sep 17 00:00:00 2001 From: Mayur Kumar Date: Mon, 11 May 2026 21:48:54 +0530 Subject: thermal/of: Fix trailing whitespace and repeated word Correct a trailing whitespace error on line 101 and remove a duplicated "which" in the kernel-doc comment for thermal_of_zone_register. Signed-off-by: Mayur Kumar Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260511161854.193573-1-kmayur809@gmail.com --- drivers/thermal/thermal_of.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 99085c806a1f..75fb7663c507 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -98,7 +98,7 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n int ret, count; *ntrips = 0; - + struct device_node *trips __free(device_node) = of_get_child_by_name(np, "trips"); if (!trips) return NULL; @@ -494,7 +494,7 @@ EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register); /** * devm_thermal_of_zone_unregister - Resource managed version of * thermal_of_zone_unregister(). - * @dev: Device for which which resource was allocated. + * @dev: Device for which resource was allocated. * @tz: a pointer to struct thermal_zone where the sensor is registered. * * This function removes the sensor callbacks and private data from the -- cgit v1.2.3 From 4f5130427e678f7cb8e78c1c18c9485e126469c0 Mon Sep 17 00:00:00 2001 From: Mayur Kumar Date: Mon, 11 May 2026 23:12:55 +0530 Subject: thermal/drivers/imx: Do not split quoted string across lines The checkpatch tool warns against splitting quoted strings across multiple lines. Join the dev_info message into a single line to improve the ability to grep for the message in the source. Signed-off-by: Mayur Kumar Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260511174255.215207-1-kmayur809@gmail.com --- drivers/thermal/imx_thermal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 38c993d1bcb3..5aaacbc53478 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -693,8 +693,8 @@ static int imx_thermal_probe(struct platform_device *pdev) goto clk_disable; } - dev_info(dev, "%s CPU temperature grade - max:%dC" - " critical:%dC passive:%dC\n", data->temp_grade, + dev_info(dev, "%s CPU temperature grade - max:%dC critical:%dC passive:%dC\n", + data->temp_grade, data->temp_max / 1000, trips[IMX_TRIP_CRITICAL].temperature / 1000, trips[IMX_TRIP_PASSIVE].temperature / 1000); -- cgit v1.2.3 From 0309075e080306ad2e3b3148a3495b10dd252ef2 Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Mon, 27 Apr 2026 15:15:15 +0800 Subject: dt-bindings: thermal: Add SpacemiT K1 thermal sensor Document the SpacemiT K1 Thermal Sensor, which supports monitoring temperatures for five zones: soc, package, gpu, cluster0, and cluster1. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Shuwei Wu Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260427-k1-thermal-v5-1-df39187480ed@mailbox.org --- .../bindings/thermal/spacemit,k1-tsensor.yaml | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml diff --git a/Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml b/Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml new file mode 100644 index 000000000000..6dad76a7dd36 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/spacemit,k1-tsensor.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/spacemit,k1-tsensor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 Thermal Sensor + +description: + The SpacemiT K1 Thermal Sensor monitors the temperature of the SoC + using multiple internal sensors (e.g., soc, package, gpu, clusters). + +maintainers: + - Shuwei Wu + +$ref: thermal-sensor.yaml# + +properties: + compatible: + const: spacemit,k1-tsensor + + reg: + maxItems: 1 + + clocks: + items: + - description: Core clock for thermal sensor + - description: Bus clock for thermal sensor + + clock-names: + items: + - const: core + - const: bus + + interrupts: + maxItems: 1 + + resets: + items: + - description: Reset for the thermal sensor + + "#thermal-sensor-cells": + const: 1 + description: + The first cell indicates the sensor ID. + 0 = soc + 1 = package + 2 = gpu + 3 = cluster0 + 4 = cluster1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - resets + - "#thermal-sensor-cells" + +additionalProperties: false + +examples: + - | + #include + + thermal@d4018000 { + compatible = "spacemit,k1-tsensor"; + reg = <0xd4018000 0x100>; + clocks = <&syscon_apbc CLK_TSEN>, + <&syscon_apbc CLK_TSEN_BUS>; + clock-names = "core", "bus"; + interrupts = <61>; + resets = <&syscon_apbc RESET_TSEN>; + #thermal-sensor-cells = <1>; + }; -- cgit v1.2.3 From 296a977f2bac45759a427dc57a21edb628f5c86c Mon Sep 17 00:00:00 2001 From: Shuwei Wu Date: Mon, 27 Apr 2026 15:15:16 +0800 Subject: thermal/drivers/spacemit/k1: Add thermal sensor support The thermal sensor on K1 supports monitoring five temperature zones. The driver registers these sensors with the thermal framework and supports standard operations: - Reading temperature (millidegree Celsius) - Setting high/low thresholds for interrupts Signed-off-by: Shuwei Wu Signed-off-by: Daniel Lezcano Reviewed-by: Anand Moon Reviewed-by: Troy Mitchell Reviewed-by: Yao Zi Tested-by: Anand Moon Tested-by: Vincent Legoll # OrangePi-RV2 Tested-by: Gong Shuai Link: https://patch.msgid.link/20260427-k1-thermal-v5-2-df39187480ed@mailbox.org --- drivers/thermal/Kconfig | 2 + drivers/thermal/Makefile | 1 + drivers/thermal/spacemit/Kconfig | 19 +++ drivers/thermal/spacemit/Makefile | 3 + drivers/thermal/spacemit/k1_tsensor.c | 280 ++++++++++++++++++++++++++++++++++ 5 files changed, 305 insertions(+) create mode 100644 drivers/thermal/spacemit/Kconfig create mode 100644 drivers/thermal/spacemit/Makefile create mode 100644 drivers/thermal/spacemit/k1_tsensor.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 40a376cf9936..810eeccedfba 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -473,6 +473,8 @@ endmenu source "drivers/thermal/renesas/Kconfig" +source "drivers/thermal/spacemit/Kconfig" + source "drivers/thermal/tegra/Kconfig" config GENERIC_ADC_THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index bb21e7ea7fc6..3b249195c088 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -65,6 +65,7 @@ obj-y += mediatek/ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o +obj-y += spacemit/ obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o diff --git a/drivers/thermal/spacemit/Kconfig b/drivers/thermal/spacemit/Kconfig new file mode 100644 index 000000000000..de7b5ece5af2 --- /dev/null +++ b/drivers/thermal/spacemit/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "SpacemiT thermal drivers" +depends on ARCH_SPACEMIT || COMPILE_TEST + +config SPACEMIT_K1_TSENSOR + tristate "SpacemiT K1 thermal sensor driver" + depends on THERMAL_OF + help + This driver provides support for the thermal sensor + integrated in the SpacemiT K1 SoC. + + The thermal sensor monitors temperatures for five thermal zones: + soc, package, gpu, cluster0, and cluster1. It supports reporting + temperature values and handling high/low threshold interrupts. + + Say Y here if you want to enable thermal monitoring on SpacemiT K1. + If compiled as a module, it will be called k1_tsensor. + +endmenu diff --git a/drivers/thermal/spacemit/Makefile b/drivers/thermal/spacemit/Makefile new file mode 100644 index 000000000000..82b30741e4ec --- /dev/null +++ b/drivers/thermal/spacemit/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SPACEMIT_K1_TSENSOR) += k1_tsensor.o diff --git a/drivers/thermal/spacemit/k1_tsensor.c b/drivers/thermal/spacemit/k1_tsensor.c new file mode 100644 index 000000000000..79222d233129 --- /dev/null +++ b/drivers/thermal/spacemit/k1_tsensor.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thermal sensor driver for SpacemiT K1 SoC + * + * Copyright (C) 2026 Shuwei Wu + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../thermal_hwmon.h" + +#define K1_TSENSOR_PCTRL_REG 0x00 +#define K1_TSENSOR_PCTRL_ENABLE BIT(0) +#define K1_TSENSOR_PCTRL_TEMP_MODE BIT(3) +#define K1_TSENSOR_PCTRL_RAW_SEL BIT(7) + +#define K1_TSENSOR_PCTRL_CTUNE GENMASK(11, 8) +#define K1_TSENSOR_PCTRL_SW_CTRL GENMASK(21, 18) +#define K1_TSENSOR_PCTRL_HW_AUTO_MODE BIT(23) + +#define K1_TSENSOR_EN_REG 0x08 +#define K1_TSENSOR_EN_ALL GENMASK(MAX_SENSOR_NUMBER - 1, 0) + +#define K1_TSENSOR_TIME_REG 0x0C +#define K1_TSENSOR_TIME_WAIT_REF_CNT GENMASK(3, 0) +#define K1_TSENSOR_TIME_ADC_CNT_RST GENMASK(7, 4) +#define K1_TSENSOR_TIME_FILTER_PERIOD GENMASK(21, 20) +#define K1_TSENSOR_TIME_MASK GENMASK(23, 0) + +#define K1_TSENSOR_INT_CLR_REG 0x10 +#define K1_TSENSOR_INT_EN_REG 0x14 +#define K1_TSENSOR_INT_STA_REG 0x18 + +#define K1_TSENSOR_INT_EN_MASK BIT(0) +#define K1_TSENSOR_INT_MASK(x) (GENMASK(2, 1) << ((x) * 2)) + +#define K1_TSENSOR_DATA_BASE_REG 0x20 +#define K1_TSENSOR_DATA_REG(x) (K1_TSENSOR_DATA_BASE_REG + ((x) / 2) * 4) +#define K1_TSENSOR_DATA_LOW_MASK GENMASK(15, 0) +#define K1_TSENSOR_DATA_HIGH_MASK GENMASK(31, 16) + +#define K1_TSENSOR_THRSH_BASE_REG 0x40 +#define K1_TSENSOR_THRSH_REG(x) (K1_TSENSOR_THRSH_BASE_REG + ((x) * 4)) +#define K1_TSENSOR_THRSH_LOW_MASK GENMASK(15, 0) +#define K1_TSENSOR_THRSH_HIGH_MASK GENMASK(31, 16) + +#define MAX_SENSOR_NUMBER 5 + +/* Hardware offset value required for temperature calculation */ +#define TEMPERATURE_OFFSET 278 + +struct k1_tsensor_channel { + struct k1_tsensor *ts; + struct thermal_zone_device *tzd; + int id; +}; + +struct k1_tsensor { + void __iomem *base; + struct k1_tsensor_channel ch[MAX_SENSOR_NUMBER]; +}; + +static void k1_tsensor_init(struct k1_tsensor *ts) +{ + u32 val; + + /* Disable all the interrupts */ + writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG); + + /* Configure ADC sampling time and filter period */ + val = readl(ts->base + K1_TSENSOR_TIME_REG); + val &= ~K1_TSENSOR_TIME_MASK; + val |= K1_TSENSOR_TIME_FILTER_PERIOD | + K1_TSENSOR_TIME_ADC_CNT_RST | + K1_TSENSOR_TIME_WAIT_REF_CNT; + writel(val, ts->base + K1_TSENSOR_TIME_REG); + + /* + * Enable all sensors' auto mode, enable dither control, + * consecutive mode, and power up sensor. + */ + val = readl(ts->base + K1_TSENSOR_PCTRL_REG); + val &= ~K1_TSENSOR_PCTRL_SW_CTRL; + val &= ~K1_TSENSOR_PCTRL_CTUNE; + val |= K1_TSENSOR_PCTRL_RAW_SEL | + K1_TSENSOR_PCTRL_TEMP_MODE | + K1_TSENSOR_PCTRL_HW_AUTO_MODE | + K1_TSENSOR_PCTRL_ENABLE; + writel(val, ts->base + K1_TSENSOR_PCTRL_REG); + + /* Enable each sensor */ + val = readl(ts->base + K1_TSENSOR_EN_REG); + val |= K1_TSENSOR_EN_ALL; + writel(val, ts->base + K1_TSENSOR_EN_REG); +} + +static void k1_tsensor_enable_irq(struct k1_tsensor_channel *ch) +{ + struct k1_tsensor *ts = ch->ts; + u32 val; + + val = readl(ts->base + K1_TSENSOR_INT_CLR_REG); + val |= K1_TSENSOR_INT_MASK(ch->id); + writel(val, ts->base + K1_TSENSOR_INT_CLR_REG); + + val = readl(ts->base + K1_TSENSOR_INT_EN_REG); + val &= ~K1_TSENSOR_INT_MASK(ch->id); + writel(val, ts->base + K1_TSENSOR_INT_EN_REG); + + /* Enable thermal interrupt */ + val = readl(ts->base + K1_TSENSOR_INT_EN_REG); + val |= K1_TSENSOR_INT_EN_MASK; + writel(val, ts->base + K1_TSENSOR_INT_EN_REG); +} + +/* + * The conversion formula used is: + * T(m°C) = (((raw_value & mask) >> shift) - TEMPERATURE_OFFSET) * 1000 + */ +static int k1_tsensor_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz); + struct k1_tsensor *ts = ch->ts; + u32 val; + + val = readl(ts->base + K1_TSENSOR_DATA_REG(ch->id)); + if (ch->id % 2) + *temp = FIELD_GET(K1_TSENSOR_DATA_HIGH_MASK, val); + else + *temp = FIELD_GET(K1_TSENSOR_DATA_LOW_MASK, val); + + *temp -= TEMPERATURE_OFFSET; + *temp *= 1000; + + return 0; +} + +/* + * For each sensor, the hardware threshold register is 32 bits: + * - Lower 16 bits [15:0] configure the low threshold temperature. + * - Upper 16 bits [31:16] configure the high threshold temperature. + */ +static int k1_tsensor_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz); + struct k1_tsensor *ts = ch->ts; + u32 val; + + if (low >= high) + return -EINVAL; + + low = clamp_val(low / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET, + FIELD_MAX(K1_TSENSOR_THRSH_LOW_MASK)); + high = clamp_val(high / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET, + FIELD_MAX(K1_TSENSOR_THRSH_HIGH_MASK)); + + val = readl(ts->base + K1_TSENSOR_THRSH_REG(ch->id)); + + val &= ~(K1_TSENSOR_THRSH_LOW_MASK | K1_TSENSOR_THRSH_HIGH_MASK); + val |= FIELD_PREP(K1_TSENSOR_THRSH_LOW_MASK, low); + val |= FIELD_PREP(K1_TSENSOR_THRSH_HIGH_MASK, high); + + writel(val, ts->base + K1_TSENSOR_THRSH_REG(ch->id)); + + return 0; +} + +static const struct thermal_zone_device_ops k1_tsensor_ops = { + .get_temp = k1_tsensor_get_temp, + .set_trips = k1_tsensor_set_trips, +}; + +static irqreturn_t k1_tsensor_irq_thread(int irq, void *data) +{ + struct k1_tsensor *ts = (struct k1_tsensor *)data; + int mask, status, i; + + status = readl(ts->base + K1_TSENSOR_INT_STA_REG); + + for (i = 0; i < MAX_SENSOR_NUMBER; i++) { + if (status & K1_TSENSOR_INT_MASK(i)) { + mask = readl(ts->base + K1_TSENSOR_INT_CLR_REG); + mask |= K1_TSENSOR_INT_MASK(i); + writel(mask, ts->base + K1_TSENSOR_INT_CLR_REG); + thermal_zone_device_update(ts->ch[i].tzd, THERMAL_EVENT_UNSPECIFIED); + } + } + + return IRQ_HANDLED; +} + +static int k1_tsensor_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k1_tsensor *ts; + struct reset_control *reset; + struct clk *clk; + int i, irq, ret; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ts->base)) + return dev_err_probe(dev, PTR_ERR(ts->base), "Failed to get reg\n"); + + reset = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), "Failed to get/deassert reset control\n"); + + clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get core clock\n"); + + clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get bus clock\n"); + + k1_tsensor_init(ts); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + k1_tsensor_irq_thread, + IRQF_ONESHOT, "k1_tsensor", ts); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_SENSOR_NUMBER; ++i) { + ts->ch[i].id = i; + ts->ch[i].ts = ts; + ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops); + if (IS_ERR(ts->ch[i].tzd)) + return PTR_ERR(ts->ch[i].tzd); + + /* Attach sysfs hwmon attributes for userspace monitoring */ + ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd); + if (ret) + dev_warn(dev, "Failed to add hwmon sysfs attributes\n"); + + k1_tsensor_enable_irq(ts->ch + i); + } + + platform_set_drvdata(pdev, ts); + + return 0; +} + +static const struct of_device_id k1_tsensor_dt_ids[] = { + { .compatible = "spacemit,k1-tsensor" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, k1_tsensor_dt_ids); + +static struct platform_driver k1_tsensor_driver = { + .driver = { + .name = "k1_tsensor", + .of_match_table = k1_tsensor_dt_ids, + }, + .probe = k1_tsensor_probe, +}; +module_platform_driver(k1_tsensor_driver); + +MODULE_DESCRIPTION("SpacemiT K1 Thermal Sensor Driver"); +MODULE_AUTHOR("Shuwei Wu "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2086b040c3d22e0c70e731047b013e31ef83ae10 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Thu, 30 Apr 2026 10:53:30 +0800 Subject: dt-bindings: thermal: qoriq: Add compatible string for imx93 Add i.MX93 compatible string 'fsl,imx93-tmu' because Thermal monitor unit(TMU) on i.MX93 has differences with QorIQ platform and not fully compatible with existing Platform, such as fsl,qoriq-tmu. Signed-off-by: Jacky Bai Signed-off-by: Daniel Lezcano Tested-by: Alexander Stein Reviewed-by: Frank Li Acked-by: Conor Dooley Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260430-imx93_tmu-v6-1-485459d7b54f@nxp.com --- Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml b/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml index aa756dae512a..f3b136f5e1cb 100644 --- a/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/qoriq-thermal.yaml @@ -25,6 +25,7 @@ properties: enum: - fsl,qoriq-tmu - fsl,imx8mq-tmu + - fsl,imx93-tmu reg: maxItems: 1 -- cgit v1.2.3 From e058b025906ee13b1cb3755073abe6ac3b297de1 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Thu, 30 Apr 2026 10:53:31 +0800 Subject: thermal/drivers/qoriq: Add i.MX93 tmu support For Thermal monitor unit(TMU) used on i.MX93, the HW revision info read from the ID register is the same the one used on some of the QorIQ platform, but the config has some slight differance. Add i.MX93 compatible string and corresponding code for it. Signed-off-by: Alice Guo Signed-off-by: Jacky Bai Signed-off-by: Daniel Lezcano Reviewed-by: Frank Li Link: https://patch.msgid.link/20260430-imx93_tmu-v6-2-485459d7b54f@nxp.com --- drivers/thermal/qoriq_thermal.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 01b58be0dcc6..e4b61d531e44 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // // Copyright 2016 Freescale Semiconductor, Inc. +// Copyright 2025 NXP #include #include @@ -24,6 +25,7 @@ #define TMTMIR_DEFAULT 0x0000000f #define TIER_DISABLE 0x0 #define TEUMR0_V2 0x51009c00 +#define TEUMR0_V21 0x55000c00 #define TMSARA_V2 0xe #define TMU_VER1 0x1 #define TMU_VER2 0x2 @@ -73,12 +75,17 @@ struct qoriq_sensor { int id; }; +struct tmu_drvdata { + u32 teumr0; +}; + struct qoriq_tmu_data { int ver; u32 ttrcr[NUM_TTRCR_MAX]; struct regmap *regmap; struct clk *clk; struct qoriq_sensor sensor[SITES_MAX]; + const struct tmu_drvdata *drvdata; }; static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) @@ -234,7 +241,8 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT); } else { regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT); - regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2); + regmap_write(data->regmap, REGS_V2_TEUMR(0), + data->drvdata->teumr0); } /* Disable monitoring */ @@ -319,6 +327,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev) data->ver = (ver >> 8) & 0xff; + data->drvdata = of_device_get_match_data(&pdev->dev); + if (!data->drvdata) + return dev_err_probe(dev, -EINVAL, "Failed to get match data\n"); + qoriq_tmu_init_device(data); /* TMU initialization */ ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ @@ -376,9 +388,22 @@ static int qoriq_tmu_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, qoriq_tmu_suspend, qoriq_tmu_resume); +static const struct tmu_drvdata qoriq_tmu_data = { + .teumr0 = TEUMR0_V2, +}; + +static const struct tmu_drvdata imx8mq_tmu_data = { + .teumr0 = TEUMR0_V2, +}; + +static const struct tmu_drvdata imx93_data = { + .teumr0 = TEUMR0_V21, +}; + static const struct of_device_id qoriq_tmu_match[] = { - { .compatible = "fsl,qoriq-tmu", }, - { .compatible = "fsl,imx8mq-tmu", }, + { .compatible = "fsl,qoriq-tmu", .data = &qoriq_tmu_data }, + { .compatible = "fsl,imx8mq-tmu", .data = &imx8mq_tmu_data }, + { .compatible = "fsl,imx93-tmu", .data = &imx93_data }, {}, }; MODULE_DEVICE_TABLE(of, qoriq_tmu_match); -- cgit v1.2.3 From 50e72d6dc427228b62d2ebce6fb0e3107527180a Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Thu, 30 Apr 2026 10:53:32 +0800 Subject: thermal/driver/qoriq: Workaround unexpected temperature readings from tmu Invalid temperature measurements may be observed across the temperature range specified in the device data sheet. The invalid temperature can be read from any remote site and from any capture or report registers. The invalid change in temperature can be positive or negative and the resulting temperature can be outside the calibrated range, in which case the TSR[ORL] or TSR[ORH] bit will be set. Workaround: Use the raising/falling edge threshold to filter out the invalid temp. Check the TIDR register to make sure no jump happens When reading the temp. i.MX93 ERR052243: (https://www.nxp.com/webapp/Download?colCode=IMX93_2P87F&appType=license) Signed-off-by: Jacky Bai Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260430-imx93_tmu-v6-3-485459d7b54f@nxp.com --- drivers/thermal/qoriq_thermal.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index e4b61d531e44..35439ec5f8bc 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -3,6 +3,7 @@ // Copyright 2016 Freescale Semiconductor, Inc. // Copyright 2025 NXP +#include #include #include #include @@ -30,6 +31,9 @@ #define TMU_VER1 0x1 #define TMU_VER2 0x2 +/* errata ID info define */ +#define TMU_ERR052243 BIT(0) + #define REGS_TMR 0x000 /* Mode Register */ #define TMR_DISABLE 0x0 #define TMR_ME 0x80000000 @@ -45,6 +49,15 @@ #define REGS_TIER 0x020 /* Interrupt Enable Register */ #define TIER_DISABLE 0x0 +#define REGS_TIDR 0x24 +#define TEMP_RATE_IRQ_MASK GENMASK(25, 24) +#define TMRTRCTR 0x70 +#define TMRTRCTR_EN BIT(31) +#define TMRTRCTR_TEMP_MASK GENMASK(7, 0) +#define TMFTRCTR 0x74 +#define TMFTRCTR_EN BIT(31) +#define TMFTRCTR_TEMP_MASK GENMASK(7, 0) +#define TEMP_RATE_THR_LVL 0x7 #define REGS_TTCFGR 0x080 /* Temperature Configuration Register */ #define REGS_TSCFGR 0x084 /* Sensor Configuration Register */ @@ -77,6 +90,7 @@ struct qoriq_sensor { struct tmu_drvdata { u32 teumr0; + u32 tmu_errata; }; struct qoriq_tmu_data { @@ -88,6 +102,12 @@ struct qoriq_tmu_data { const struct tmu_drvdata *drvdata; }; +static inline bool qoriq_tmu_has_errata(const struct tmu_drvdata *drvdata, + u32 flag) +{ + return drvdata->tmu_errata & flag; +} + static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) { return container_of(s, struct qoriq_tmu_data, sensor[s->id]); @@ -97,7 +117,7 @@ static int tmu_get_temp(struct thermal_zone_device *tz, int *temp) { struct qoriq_sensor *qsensor = thermal_zone_device_priv(tz); struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); - u32 val; + u32 val, tidr; /* * REGS_TRITSR(id) has the following layout: * @@ -130,6 +150,15 @@ static int tmu_get_temp(struct thermal_zone_device *tz, int *temp) 10 * USEC_PER_MSEC)) return -ENODATA; + /*ERR052243: If a raising or falling edge happens, try later */ + if (qoriq_tmu_has_errata(qdata->drvdata, TMU_ERR052243)) { + regmap_read(qdata->regmap, REGS_TIDR, &tidr); + if (tidr & TEMP_RATE_IRQ_MASK) { + regmap_write(qdata->regmap, REGS_TIDR, TEMP_RATE_IRQ_MASK); + return -EAGAIN; + } + } + if (qdata->ver == TMU_VER1) { *temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE; } else { @@ -245,6 +274,14 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) data->drvdata->teumr0); } + /* ERR052243: Set the raising & falling edge monitor */ + if (qoriq_tmu_has_errata(data->drvdata, TMU_ERR052243)) { + regmap_write(data->regmap, TMRTRCTR, TMRTRCTR_EN | + FIELD_PREP(TMRTRCTR_TEMP_MASK, TEMP_RATE_THR_LVL)); + regmap_write(data->regmap, TMFTRCTR, TMFTRCTR_EN | + FIELD_PREP(TMFTRCTR_TEMP_MASK, TEMP_RATE_THR_LVL)); + + } /* Disable monitoring */ regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); } @@ -398,6 +435,7 @@ static const struct tmu_drvdata imx8mq_tmu_data = { static const struct tmu_drvdata imx93_data = { .teumr0 = TEUMR0_V21, + .tmu_errata = TMU_ERR052243, }; static const struct of_device_id qoriq_tmu_match[] = { -- cgit v1.2.3 From de4483d4447981292c68d45ea643158bb8ca92b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 26 May 2026 15:53:13 +0200 Subject: thermal/drivers/samsung: Enable TMU by default The SoC Thermal Management Unit (TMU) is essential for proper operation of the SoCs. Kernel should not ask users choice of drivers when that choice is obvious and known to the developers that answer should be 'yes' or 'module'. Impact of making it default: 1. arm64 defconfig: No changes, already present in defconfig. 2. arm32: No changes, the driver is already selected by MACH_EXYNOS. 3. COMPILE_TEST builds: enable by default for arm32 or arm64 builds, whenever ARCH_EXYNOS is selected. This has impact on build time and feels logical, because if one selects ARCH_EXYNOS then probably by default wants to build test it entirely. Kernels with COMPILE_TEST are not supposed to be used for booting. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260526135312.8697-2-krzysztof.kozlowski@oss.qualcomm.com --- drivers/thermal/samsung/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig index f4eff5a41a84..e1e8035d24fb 100644 --- a/drivers/thermal/samsung/Kconfig +++ b/drivers/thermal/samsung/Kconfig @@ -3,6 +3,7 @@ config EXYNOS_THERMAL tristate "Exynos thermal management unit driver" depends on THERMAL_OF depends on HAS_IOMEM + default ARCH_EXYNOS help If you say yes here you get support for the TMU (Thermal Management Unit) driver for Samsung Exynos series of SoCs. This driver initialises -- cgit v1.2.3 From 298a2d461f1ffd75e4ac06774f42e1f018c3a840 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:02 +0200 Subject: thermal/core: Introduce non-OF thermal_cooling_device_register() Split the cooling device registration API into OF and non-OF variants. Introduce thermal_cooling_device_register() for non-device-tree users and rework thermal_of_cooling_device_register() to use the new alloc/add split. This removes the need for the internal __thermal_cooling_device_register() helper and makes the separation between OF and non-OF users explicit. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-13-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/thermal_core.c | 60 +++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index db01361569d7..0b3db889d60d 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1059,8 +1059,7 @@ out_put_device: } /** - * __thermal_cooling_device_register() - register a new thermal cooling device - * @np: a pointer to a device tree node. + * thermal_cooling_device_register() - register a new thermal cooling device * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. @@ -1068,16 +1067,13 @@ out_put_device: * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. - * It also gives the opportunity to link the cooling device to a device tree - * node, so that it can be bound to a thermal zone created out of device tree. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ -static struct thermal_cooling_device * -__thermal_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +struct thermal_cooling_device * +thermal_cooling_device_register(const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; int ret; @@ -1086,34 +1082,12 @@ __thermal_cooling_device_register(struct device_node *np, if (IS_ERR(cdev)) return cdev; - cdev->np = np; - ret = thermal_cooling_device_add(cdev, devdata); if (ret) return ERR_PTR(ret); return cdev; } - -/** - * thermal_cooling_device_register() - register a new thermal cooling device - * @type: the thermal cooling device type. - * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. - * - * This interface function adds a new thermal cooling device (fan/processor/...) - * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself - * to all the thermal zone devices registered at the same time. - * - * Return: a pointer to the created struct thermal_cooling_device or an - * ERR_PTR. Caller must check return value with IS_ERR*() helpers. - */ -struct thermal_cooling_device * -thermal_cooling_device_register(const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) -{ - return __thermal_cooling_device_register(NULL, type, devdata, ops); -} EXPORT_SYMBOL_GPL(thermal_cooling_device_register); /** @@ -1121,22 +1095,36 @@ EXPORT_SYMBOL_GPL(thermal_cooling_device_register); * @np: a pointer to a device tree node. * @type: the thermal cooling device type. * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. + * @ops: standard thermal cooling devices callbacks. * - * This function will register a cooling device with device tree node reference. * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. + * It also gives the opportunity to link the cooling device to a device tree + * node, so that it can be bound to a thermal zone created out of device tree. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * thermal_of_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) { - return __thermal_cooling_device_register(np, type, devdata, ops); + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_cooling_device_alloc(type, ops); + if (IS_ERR(cdev)) + return cdev; + + cdev->np = np; + + ret = thermal_cooling_device_add(cdev, devdata); + if (ret) + return ERR_PTR(ret); + + return cdev; } EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); @@ -1173,7 +1161,7 @@ devm_thermal_of_cooling_device_register(struct device *dev, struct thermal_cooling_device *cdev; int ret; - cdev = __thermal_cooling_device_register(np, type, devdata, ops); + cdev = thermal_of_cooling_device_register(np, type, devdata, ops); if (IS_ERR(cdev)) return cdev; -- cgit v1.2.3 From 876bb45f36939307c1e376243d862ad1b1a5f0cd Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:03 +0200 Subject: thermal/core: Add devm_thermal_cooling_device_register() Introduce a device-managed variant of the non-OF cooling device registration API. This complements devm_thermal_of_cooling_device_register() and allows non-device-tree users to register cooling devices with automatic cleanup tied to the device lifecycle. The helper relies on devm_add_action_or_reset() to release the cooling device via thermal_cooling_device_release() on driver detach or probe failure. This keeps the API consistent across OF and non-OF users and avoids manual cleanup in error paths. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-14-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/thermal_core.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/thermal.h | 7 +++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 0b3db889d60d..bb4fc3ff2ad5 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1173,6 +1173,41 @@ devm_thermal_of_cooling_device_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); +/** + * devm_thermal_cooling_device_register() - register a thermal cooling device + * + * @dev: a valid struct device pointer of a sensor device. + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This function will register a cooling device. This interface + * function adds a new thermal cooling device (fan/processor/...) to + * /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind + * itself to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +devm_thermal_cooling_device_register(struct device *dev, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_cooling_device_register(type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; + + ret = devm_add_action_or_reset(dev, thermal_cooling_device_release, cdev); + if (ret) + return ERR_PTR(ret); + + return cdev; +} +EXPORT_SYMBOL_GPL(devm_thermal_cooling_device_register); + static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) { struct thermal_cooling_device *pos = NULL; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 0ddc77aeeca2..fc3f4a098370 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -253,8 +253,11 @@ void thermal_zone_device_update(struct thermal_zone_device *, struct thermal_cooling_device *thermal_cooling_device_register(const char *, void *, const struct thermal_cooling_device_ops *); struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, const char *, void *, - const struct thermal_cooling_device_ops *); +devm_thermal_cooling_device_register(struct device *dev, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); +struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); struct thermal_cooling_device * devm_thermal_of_cooling_device_register(struct device *dev, struct device_node *np, -- cgit v1.2.3 From 61e7550fe8b26c2b132eff2ced57c6b2dd93ca7f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:04 +0200 Subject: hwmon: Use non-OF thermal cooling device registration API Some HWMON drivers register cooling devices using the OF helper devm_thermal_of_cooling_device_register() with a NULL device node. With the introduction of a dedicated non-OF registration API, switch these users to devm_thermal_cooling_device_register() to make the intent explicit and avoid relying on OF-specific helpers. This is a pure refactoring with no functional change. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Guenter Roeck Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-15-daniel.lezcano@oss.qualcomm.com --- drivers/hwmon/cros_ec_hwmon.c | 4 ++-- drivers/hwmon/dell-smm-hwmon.c | 4 ++-- drivers/hwmon/mlxreg-fan.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index 6cf5ab0f4b73..77dd9f28962d 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -532,8 +532,8 @@ static void cros_ec_hwmon_register_fan_cooling_devices(struct device *dev, cpriv->hwmon_priv = priv; cpriv->index = i; - cdev = devm_thermal_of_cooling_device_register(dev, NULL, type, cpriv, - &cros_ec_thermal_cooling_ops); + cdev = devm_thermal_cooling_device_register(dev, type, cpriv, + &cros_ec_thermal_cooling_ops); if (IS_ERR(cdev)) { dev_warn(dev, "failed to register fan %zu as a cooling device: %pe\n", i, cdev); diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 038edffc1ac7..47b373ea6db4 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1161,8 +1161,8 @@ static int dell_smm_init_cdev(struct device *dev, u8 fan_num) if (cdata) { cdata->fan_num = fan_num; cdata->data = data; - cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata, - &dell_smm_cooling_ops); + cdev = devm_thermal_cooling_device_register(dev, name, cdata, + &dell_smm_cooling_ops); if (IS_ERR(cdev)) { devm_kfree(dev, cdata); ret = PTR_ERR(cdev); diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index 137a90dd2075..860de6cfd8a4 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -583,8 +583,8 @@ static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan) pwm->fan = fan; /* Set minimal PWM speed. */ pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY); - pwm->cdev = devm_thermal_of_cooling_device_register(dev, NULL, mlxreg_fan_name[i], - pwm, &mlxreg_fan_cooling_ops); + pwm->cdev = devm_thermal_cooling_device_register(dev, mlxreg_fan_name[i], + pwm, &mlxreg_fan_cooling_ops); if (IS_ERR(pwm->cdev)) { dev_err(dev, "Failed to register cooling device\n"); return PTR_ERR(pwm->cdev); -- cgit v1.2.3 From 27559121b2e3ffbdfdbb77b528ba1015e2617daa Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:06 +0200 Subject: thermal/of: Move cooling device OF helpers out of thermal core The functions: - thermal_of_cooling_device_register() - devm_thermal_of_cooling_device_register() are specific to device tree usage but are currently implemented in thermal_core.c. Move them to thermal_of.c to better reflect the separation between generic thermal core code and OF-specific logic. This change is enabled by the recent split of the cooling device registration into allocation and addition phases, allowing OF-specific handling (such as device node assignment) to be isolated from the core. No functional change intended. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-17-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/thermal_core.c | 80 +--------------------------------------- drivers/thermal/thermal_core.h | 5 +++ drivers/thermal/thermal_of.c | 83 ++++++++++++++++++++++++++++++++++++++++++ include/linux/thermal.h | 49 +++++++++++++++---------- 4 files changed, 119 insertions(+), 98 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index bb4fc3ff2ad5..28a20d4b475c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -963,7 +963,7 @@ static void thermal_cdev_release(struct device *dev) kfree(cdev); } -static struct thermal_cooling_device * +struct thermal_cooling_device * thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; @@ -1002,7 +1002,7 @@ out_kfree_cdev: return ERR_PTR(ret); } -static int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata) +int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata) { unsigned long current_state; int ret; @@ -1090,44 +1090,6 @@ thermal_cooling_device_register(const char *type, void *devdata, } EXPORT_SYMBOL_GPL(thermal_cooling_device_register); -/** - * thermal_of_cooling_device_register() - register an OF thermal cooling device - * @np: a pointer to a device tree node. - * @type: the thermal cooling device type. - * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. - * - * This interface function adds a new thermal cooling device (fan/processor/...) - * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself - * to all the thermal zone devices registered at the same time. - * It also gives the opportunity to link the cooling device to a device tree - * node, so that it can be bound to a thermal zone created out of device tree. - * - * Return: a pointer to the created struct thermal_cooling_device or an - * ERR_PTR. Caller must check return value with IS_ERR*() helpers. - */ -struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) -{ - struct thermal_cooling_device *cdev; - int ret; - - cdev = thermal_cooling_device_alloc(type, ops); - if (IS_ERR(cdev)) - return cdev; - - cdev->np = np; - - ret = thermal_cooling_device_add(cdev, devdata); - if (ret) - return ERR_PTR(ret); - - return cdev; -} -EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); - static void thermal_cooling_device_release(void *data) { struct thermal_cooling_device *cdev = data; @@ -1135,44 +1097,6 @@ static void thermal_cooling_device_release(void *data) thermal_cooling_device_unregister(cdev); } -/** - * devm_thermal_of_cooling_device_register() - register an OF thermal cooling - * device - * @dev: a valid struct device pointer of a sensor device. - * @np: a pointer to a device tree node. - * @type: the thermal cooling device type. - * @devdata: device private data. - * @ops: standard thermal cooling devices callbacks. - * - * This function will register a cooling device with device tree node reference. - * This interface function adds a new thermal cooling device (fan/processor/...) - * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself - * to all the thermal zone devices registered at the same time. - * - * Return: a pointer to the created struct thermal_cooling_device or an - * ERR_PTR. Caller must check return value with IS_ERR*() helpers. - */ -struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) -{ - struct thermal_cooling_device *cdev; - int ret; - - cdev = thermal_of_cooling_device_register(np, type, devdata, ops); - if (IS_ERR(cdev)) - return cdev; - - ret = devm_add_action_or_reset(dev, thermal_cooling_device_release, cdev); - if (ret) - return ERR_PTR(ret); - - return cdev; -} -EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); - /** * devm_thermal_cooling_device_register() - register a thermal cooling device * diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 0acb7d9587ca..e98b0aa5aacc 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -267,6 +267,11 @@ void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz); void thermal_governor_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason); +struct thermal_cooling_device * +thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops); + +int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata); + /* Helpers */ #define for_each_trip_desc(__tz, __td) \ for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 75fb7663c507..8c49d449d43f 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -510,3 +510,86 @@ void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_dev devm_thermal_of_zone_match, tz)); } EXPORT_SYMBOL_GPL(devm_thermal_of_zone_unregister); + +/** + * thermal_of_cooling_device_register() - register an OF thermal cooling device + * @np: a pointer to a device tree node. + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * It also gives the opportunity to link the cooling device to a device tree + * node, so that it can be bound to a thermal zone created out of device tree. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_cooling_device_alloc(type, ops); + if (IS_ERR(cdev)) + return cdev; + + cdev->np = np; + + ret = thermal_cooling_device_add(cdev, devdata); + if (ret) + return ERR_PTR(ret); + + return cdev; +} +EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); + +static void thermal_of_cooling_device_release(void *data) +{ + struct thermal_cooling_device *cdev = data; + + thermal_cooling_device_unregister(cdev); +} + +/** + * devm_thermal_of_cooling_device_register() - register an OF thermal cooling + * device + * @dev: a valid struct device pointer of a sensor device. + * @np: a pointer to a device tree node. + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This function will register a cooling device with device tree node reference. + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_of_cooling_device_register(np, type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; + + ret = devm_add_action_or_reset(dev, thermal_of_cooling_device_release, cdev); + if (ret) + return ERR_PTR(ret); + + return cdev; +} +EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index fc3f4a098370..3e663267a19d 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -198,6 +198,15 @@ struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, in void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz); +struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); + +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); #else static inline @@ -211,6 +220,23 @@ static inline void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz) { } + +static inline struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return ERR_PTR(-ENODEV); +} #endif int for_each_thermal_trip(struct thermal_zone_device *tz, @@ -252,17 +278,11 @@ void thermal_zone_device_update(struct thermal_zone_device *, struct thermal_cooling_device *thermal_cooling_device_register(const char *, void *, const struct thermal_cooling_device_ops *); + struct thermal_cooling_device * devm_thermal_cooling_device_register(struct device *dev, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops); -struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops); -struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops); + void thermal_cooling_device_update(struct thermal_cooling_device *); void thermal_cooling_device_unregister(struct thermal_cooling_device *); struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name); @@ -308,18 +328,7 @@ thermal_cooling_device_register(const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { return ERR_PTR(-ENODEV); } static inline struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) -{ return ERR_PTR(-ENODEV); } -static inline struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) -{ - return ERR_PTR(-ENODEV); -} + static inline void thermal_cooling_device_unregister( struct thermal_cooling_device *cdev) { } -- cgit v1.2.3 From 5fa8b4225bec1fde0862a2d19964429662841384 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:05 +0200 Subject: thermal/core: Make cooling device OF node conditional on CONFIG_THERMAL_OF The device node pointer stored in struct thermal_cooling_device is only used by the OF-specific thermal code to associate cooling devices with thermal zones defined in device tree. Now that OF and non-OF registration paths are separated and non-OF users no longer rely on devm_thermal_of_cooling_device_register() with a NULL device node, the np field is no longer required for non-OF configurations. Make this field conditional on CONFIG_THERMAL_OF to reduce memory footprint and better reflect its usage. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Acked-by: Rafael J. Wysocki (Intel) Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/20260526140802.1059293-16-daniel.lezcano@oss.qualcomm.com --- include/linux/thermal.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 3e663267a19d..6d1862ac187f 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -125,7 +125,6 @@ struct thermal_cooling_device { const char *type; unsigned long max_state; struct device device; - struct device_node *np; void *devdata; void *stats; const struct thermal_cooling_device_ops *ops; @@ -133,6 +132,9 @@ struct thermal_cooling_device { struct mutex lock; /* protect thermal_instances list */ struct list_head thermal_instances; struct list_head node; +#ifdef CONFIG_THERMAL_OF + struct device_node *np; +#endif #ifdef CONFIG_THERMAL_DEBUGFS struct thermal_debugfs *debugfs; #endif -- cgit v1.2.3 From 8e1529e79385608002e126730d32bd91d7427795 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:07 +0200 Subject: thermal/of: Rename the devm_thermal_of_cooling_device_register() function To clarify that the function operates on child nodes, rename: devm_thermal_of_cooling_device_register() | v devm_thermal_of_child_cooling_device_register() Used the command: find . -type f -name '*.[ch]' -exec \ sed -i 's/devm_thermal_of_cooling_device_register/\ devm_thermal_of_child_cooling_device_register/g' {} \; Did not used clang-format-diff because it does not indent correctly and checkpatch complained. Manually reindented to make checkpatch happy This prepares for upcoming support of cooling devices identified by an ID rather than device tree child nodes. No functional change. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-18-daniel.lezcano@oss.qualcomm.com --- drivers/hwmon/amc6821.c | 2 +- drivers/hwmon/aspeed-pwm-tacho.c | 5 +++-- drivers/hwmon/emc2305.c | 6 +++--- drivers/hwmon/gpio-fan.c | 6 ++++-- drivers/hwmon/max6650.c | 6 +++--- drivers/hwmon/npcm750-pwm-fan.c | 6 ++++-- drivers/hwmon/pwm-fan.c | 5 +++-- drivers/hwmon/qnap-mcu-hwmon.c | 6 +++--- drivers/hwmon/tc654.c | 5 +++-- drivers/memory/tegra/tegra210-emc-core.c | 4 ++-- drivers/soc/qcom/qcom_aoss.c | 2 +- drivers/thermal/khadas_mcu_fan.c | 7 ++++--- drivers/thermal/tegra/soctherm.c | 6 +++--- drivers/thermal/thermal_of.c | 15 +++++++++------ include/linux/thermal.h | 16 ++++++++-------- 15 files changed, 54 insertions(+), 43 deletions(-) diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index d5f864b360b0..8e5926b06070 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -1076,7 +1076,7 @@ static int amc6821_probe(struct i2c_client *client) "Failed to initialize hwmon\n"); if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels) - return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev, + return PTR_ERR_OR_ZERO(devm_thermal_of_child_cooling_device_register(dev, fan_np, client->name, data, &amc6821_cooling_ops)); return 0; diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index aa159bf158a3..1c5945d4ba37 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -841,8 +841,9 @@ static int aspeed_create_pwm_cooling(struct device *dev, } snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port); - cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, - cdev->name, cdev, &aspeed_pwm_cool_ops); + cdev->tcdev = devm_thermal_of_child_cooling_device_register(dev, child, + cdev->name, cdev, + &aspeed_pwm_cool_ops); if (IS_ERR(cdev->tcdev)) return PTR_ERR(cdev->tcdev); diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 64b213e1451e..2505e9fac499 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -309,9 +309,9 @@ static int emc2305_set_single_tz(struct device *dev, struct device_node *fan_nod pwm = data->pwm_min[cdev_idx]; data->cdev_data[cdev_idx].cdev = - devm_thermal_of_cooling_device_register(dev, fan_node, - emc2305_fan_name[idx], data, - &emc2305_cooling_ops); + devm_thermal_of_child_cooling_device_register(dev, fan_node, + emc2305_fan_name[idx], data, + &emc2305_cooling_ops); if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index a8892ced1e54..084828e1e281 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -592,8 +592,10 @@ static int gpio_fan_probe(struct platform_device *pdev) } /* Optional cooling device register for Device tree platforms */ - fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np, - "gpio-fan", fan_data, &gpio_fan_cool_ops); + fan_data->cdev = devm_thermal_of_child_cooling_device_register(dev, np, + "gpio-fan", + fan_data, + &gpio_fan_cool_ops); dev_info(dev, "GPIO fan initialized\n"); diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 56b8157885bb..3466edd7d501 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -794,9 +794,9 @@ static int max6650_probe(struct i2c_client *client) return err; if (IS_ENABLED(CONFIG_THERMAL)) { - cooling_dev = devm_thermal_of_cooling_device_register(dev, - dev->of_node, client->name, - data, &max6650_cooling_ops); + cooling_dev = devm_thermal_of_child_cooling_device_register(dev, dev->of_node, + client->name, data, + &max6650_cooling_ops); if (IS_ERR(cooling_dev)) { dev_warn(dev, "thermal cooling device register failed: %ld\n", PTR_ERR(cooling_dev)); diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index c8f5e695fb6d..aea0b8659f5f 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -857,8 +857,10 @@ static int npcm7xx_create_pwm_cooling(struct device *dev, snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child, pwm_port); - cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, - cdev->name, cdev, &npcm7xx_pwm_cool_ops); + cdev->tcdev = devm_thermal_of_child_cooling_device_register(dev, child, + cdev->name, + cdev, + &npcm7xx_pwm_cool_ops); if (IS_ERR(cdev->tcdev)) return PTR_ERR(cdev->tcdev); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 37269db2de84..e6a567d58579 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -685,8 +685,9 @@ static int pwm_fan_probe(struct platform_device *pdev) ctx->pwm_fan_state = ctx->pwm_fan_max_state; if (IS_ENABLED(CONFIG_THERMAL)) { - cdev = devm_thermal_of_cooling_device_register(dev, - dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev, dev->of_node, + "pwm-fan", ctx, + &pwm_fan_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); dev_err(dev, diff --git a/drivers/hwmon/qnap-mcu-hwmon.c b/drivers/hwmon/qnap-mcu-hwmon.c index e86e64c4d391..c1c1e9d6f340 100644 --- a/drivers/hwmon/qnap-mcu-hwmon.c +++ b/drivers/hwmon/qnap-mcu-hwmon.c @@ -337,9 +337,9 @@ static int qnap_mcu_hwmon_probe(struct platform_device *pdev) * levels and only succeed with either no or correct cooling levels. */ if (IS_ENABLED(CONFIG_THERMAL) && hwm->fan_cooling_levels) { - cdev = devm_thermal_of_cooling_device_register(dev, - to_of_node(hwm->fan_node), "qnap-mcu-hwmon", - hwm, &qnap_mcu_hwmon_cooling_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev, to_of_node(hwm->fan_node), + "qnap-mcu-hwmon", hwm, + &qnap_mcu_hwmon_cooling_ops); if (IS_ERR(cdev)) return dev_err_probe(dev, PTR_ERR(cdev), "Failed to register qnap-mcu-hwmon as cooling device\n"); diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c index 39fe5836f237..ba18b442b81e 100644 --- a/drivers/hwmon/tc654.c +++ b/drivers/hwmon/tc654.c @@ -541,8 +541,9 @@ static int tc654_probe(struct i2c_client *client) if (IS_ENABLED(CONFIG_THERMAL)) { struct thermal_cooling_device *cdev; - cdev = devm_thermal_of_cooling_device_register(dev, dev->of_node, client->name, - hwmon_dev, &tc654_fan_cool_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev, dev->of_node, + client->name, hwmon_dev, + &tc654_fan_cool_ops); return PTR_ERR_OR_ZERO(cdev); } diff --git a/drivers/memory/tegra/tegra210-emc-core.c b/drivers/memory/tegra/tegra210-emc-core.c index e96ca4157d48..065ae8bc2830 100644 --- a/drivers/memory/tegra/tegra210-emc-core.c +++ b/drivers/memory/tegra/tegra210-emc-core.c @@ -1966,8 +1966,8 @@ static int tegra210_emc_probe(struct platform_device *pdev) tegra210_emc_debugfs_init(emc); - cd = devm_thermal_of_cooling_device_register(emc->dev, np, "emc", emc, - &tegra210_emc_cd_ops); + cd = devm_thermal_of_child_cooling_device_register(emc->dev, np, "emc", emc, + &tegra210_emc_cd_ops); if (IS_ERR(cd)) { err = PTR_ERR(cd); dev_err(emc->dev, "failed to register cooling device: %d\n", diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index c255662b8fc3..259c41f0c34e 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -381,7 +381,7 @@ static int qmp_cooling_device_add(struct qmp *qmp, qmp_cdev->qmp = qmp; qmp_cdev->state = !qmp_cdev_max_state; qmp_cdev->name = cdev_name; - qmp_cdev->cdev = devm_thermal_of_cooling_device_register + qmp_cdev->cdev = devm_thermal_of_child_cooling_device_register (qmp->dev, node, cdev_name, qmp_cdev, &qmp_cooling_device_ops); diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c index d35e5313bea4..21b3d0a71bd0 100644 --- a/drivers/thermal/khadas_mcu_fan.c +++ b/drivers/thermal/khadas_mcu_fan.c @@ -90,9 +90,10 @@ static int khadas_mcu_fan_probe(struct platform_device *pdev) ctx->mcu = mcu; platform_set_drvdata(pdev, ctx); - cdev = devm_thermal_of_cooling_device_register(dev->parent, - dev->parent->of_node, "khadas-mcu-fan", ctx, - &khadas_mcu_fan_cooling_ops); + cdev = devm_thermal_of_child_cooling_device_register(dev->parent, + dev->parent->of_node, + "khadas-mcu-fan", ctx, + &khadas_mcu_fan_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n", diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 6a56638c98f1..d8e988a0d43e 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1707,9 +1707,9 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) stc->init = true; } else { - tcd = devm_thermal_of_cooling_device_register(dev, np_stcc, - (char *)name, ts, - &throt_cooling_ops); + tcd = devm_thermal_of_child_cooling_device_register(dev, np_stcc, + (char *)name, ts, + &throt_cooling_ops); if (IS_ERR_OR_NULL(tcd)) { dev_err(dev, "throttle-cfg: %s: failed to register cooling device\n", diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 8c49d449d43f..b59d2588ff7a 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -557,7 +557,7 @@ static void thermal_of_cooling_device_release(void *data) } /** - * devm_thermal_of_cooling_device_register() - register an OF thermal cooling + * devm_thermal_of_child_cooling_device_register() - register an OF thermal cooling * device * @dev: a valid struct device pointer of a sensor device. * @np: a pointer to a device tree node. @@ -570,14 +570,17 @@ static void thermal_of_cooling_device_release(void *data) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. * + * This function should be used when a cooling controller has child + * nodes which are referenced in the thermal zone cooling map. + * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +devm_thermal_of_child_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; int ret; @@ -592,4 +595,4 @@ devm_thermal_of_cooling_device_register(struct device *dev, return cdev; } -EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); +EXPORT_SYMBOL_GPL(devm_thermal_of_child_cooling_device_register); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 6d1862ac187f..e6328234a42b 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -205,10 +205,10 @@ thermal_of_cooling_device_register(struct device_node *np, const char *type, voi const struct thermal_cooling_device_ops *ops); struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops); +devm_thermal_of_child_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); #else static inline @@ -232,10 +232,10 @@ thermal_of_cooling_device_register(struct device_node *np, } static inline struct thermal_cooling_device * -devm_thermal_of_cooling_device_register(struct device *dev, - struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +devm_thermal_of_child_cooling_device_register(struct device *dev, + struct device_node *np, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) { return ERR_PTR(-ENODEV); } -- cgit v1.2.3 From 37324803f049bee0d2b97941e3fbdf35db9a66b3 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:08 +0200 Subject: thermal/of: Add cooling device ID support Introduce an identifier (cdev_id) for cooling devices registered from device tree. This prepares support for a new DT binding where cooling devices are identified by a tuple (device node, ID), instead of relying on child nodes. Existing users are updated to pass a default ID of 0, preserving the current behavior. Future changes will extend the cooling map parsing to match cooling devices based on both the device node and the ID. No functional change intended. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-19-daniel.lezcano@oss.qualcomm.com --- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 5 +++-- drivers/thermal/cpufreq_cooling.c | 2 +- drivers/thermal/cpuidle_cooling.c | 2 +- drivers/thermal/devfreq_cooling.c | 2 +- drivers/thermal/thermal_of.c | 14 ++++++++------ include/linux/thermal.h | 6 ++++-- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index a891d4f1f843..552631c3554a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1791,8 +1791,9 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master, int ret; if (IS_ENABLED(CONFIG_DRM_ETNAVIV_THERMAL)) { - gpu->cooling = thermal_of_cooling_device_register(dev->of_node, - (char *)dev_name(dev), gpu, &cooling_ops); + gpu->cooling = thermal_of_cooling_device_register(dev->of_node, 0, + dev_name(dev), + gpu, &cooling_ops); if (IS_ERR(gpu->cooling)) return PTR_ERR(gpu->cooling); } diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c index 32bf5ab44f4a..768859a7aed0 100644 --- a/drivers/thermal/cpufreq_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -592,7 +592,7 @@ __cpufreq_cooling_register(struct device_node *np, if (!name) goto remove_qos_req; - cdev = thermal_of_cooling_device_register(np, name, cpufreq_cdev, + cdev = thermal_of_cooling_device_register(np, 0, name, cpufreq_cdev, cooling_ops); kfree(name); diff --git a/drivers/thermal/cpuidle_cooling.c b/drivers/thermal/cpuidle_cooling.c index 425f596614e8..bbd2e91cf5ab 100644 --- a/drivers/thermal/cpuidle_cooling.c +++ b/drivers/thermal/cpuidle_cooling.c @@ -207,7 +207,7 @@ static int __cpuidle_cooling_register(struct device_node *np, goto out_unregister; } - cdev = thermal_of_cooling_device_register(np, name, idle_cdev, + cdev = thermal_of_cooling_device_register(np, 0, name, idle_cdev, &cpuidle_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 1c7dffc8d45f..0330a8112832 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -454,7 +454,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, if (!name) goto remove_qos_req; - cdev = thermal_of_cooling_device_register(np, name, dfc, ops); + cdev = thermal_of_cooling_device_register(np, 0, name, dfc, ops); kfree(name); if (IS_ERR(cdev)) { diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index b59d2588ff7a..0110b195f7a3 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -514,6 +514,7 @@ EXPORT_SYMBOL_GPL(devm_thermal_of_zone_unregister); /** * thermal_of_cooling_device_register() - register an OF thermal cooling device * @np: a pointer to a device tree node. + * @cdev_id: a cooling device id in the cooling controller * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. @@ -528,9 +529,9 @@ EXPORT_SYMBOL_GPL(devm_thermal_of_zone_unregister); * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, - const char *type, void *devdata, - const struct thermal_cooling_device_ops *ops) +thermal_of_cooling_device_register(struct device_node *np, u32 cdev_id, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; int ret; @@ -540,6 +541,7 @@ thermal_of_cooling_device_register(struct device_node *np, return cdev; cdev->np = np; + cdev->cdev_id = cdev_id; ret = thermal_cooling_device_add(cdev, devdata); if (ret) @@ -585,9 +587,9 @@ devm_thermal_of_child_cooling_device_register(struct device *dev, struct thermal_cooling_device *cdev; int ret; - cdev = thermal_of_cooling_device_register(np, type, devdata, ops); - if (IS_ERR(cdev)) - return cdev; + cdev = thermal_of_cooling_device_register(np, 0, type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; ret = devm_add_action_or_reset(dev, thermal_of_cooling_device_release, cdev); if (ret) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index e6328234a42b..fb7649439dfa 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -134,6 +134,7 @@ struct thermal_cooling_device { struct list_head node; #ifdef CONFIG_THERMAL_OF struct device_node *np; + u32 cdev_id; #endif #ifdef CONFIG_THERMAL_DEBUGFS struct thermal_debugfs *debugfs; @@ -201,7 +202,8 @@ struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, in void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz); struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, const char *type, void *devdata, +thermal_of_cooling_device_register(struct device_node *np, u32 cdev_id, + const char *type, void *data, const struct thermal_cooling_device_ops *ops); struct thermal_cooling_device * @@ -224,7 +226,7 @@ static inline void devm_thermal_of_zone_unregister(struct device *dev, } static inline struct thermal_cooling_device * -thermal_of_cooling_device_register(struct device_node *np, +thermal_of_cooling_device_register(struct device_node *np, u32 cdev_id, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { -- cgit v1.2.3 From 3570cb58e3171c8a65d2cedc0371ed412e0caff6 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:09 +0200 Subject: thermal/of: Pass cdev_id and introduce devm registration helper Extend the OF cooling device registration to support an explicit cooling device identifier (cdev_id), preparing for upcoming DT bindings where cooling devices are identified by a tuple (device node, id) instead of relying on child nodes. Introduce a new helper: devm_thermal_of_cooling_device_register() which registers a cooling device using the device's of_node and an explicit cdev_id. This complements the existing devm_thermal_of_child_cooling_device_register() helper, which remains dedicated to the legacy child-node based bindings. Internally, factorize the devm registration logic into a common helper to avoid code duplication. Existing users are unaffected, as the child-based helper continues to pass a default cdev_id of 0, preserving current behavior. This change is a preparatory step for supporting indexed cooling devices in thermal OF bindings. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-20-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/thermal_of.c | 58 +++++++++++++++++++++++++++++++++++--------- include/linux/thermal.h | 13 ++++++++++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 0110b195f7a3..3584024b76f5 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -558,6 +558,51 @@ static void thermal_of_cooling_device_release(void *data) thermal_cooling_device_unregister(cdev); } +static struct thermal_cooling_device * +__devm_thermal_of_cooling_device_register(struct device *dev, struct device_node *np, + u32 cdev_id, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device *cdev; + int ret; + + cdev = thermal_of_cooling_device_register(np, cdev_id, type, devdata, ops); + if (IS_ERR(cdev)) + return cdev; + + ret = devm_add_action_or_reset(dev, thermal_of_cooling_device_release, cdev); + if (ret) + return ERR_PTR(ret); + + return cdev; +} + +/** + * devm_thermal_of_cooling_device_register() - register an OF thermal cooling device + * @dev: a valid struct device pointer of a sensor device. + * @cdev_id: a cooling device index in the cooling controller + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This function will register a cooling device with device tree node reference. + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, u32 cdev_id, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return __devm_thermal_of_cooling_device_register(dev, dev->of_node, cdev_id, + type, devdata, ops); +} +EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); + /** * devm_thermal_of_child_cooling_device_register() - register an OF thermal cooling * device @@ -584,17 +629,6 @@ devm_thermal_of_child_cooling_device_register(struct device *dev, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { - struct thermal_cooling_device *cdev; - int ret; - - cdev = thermal_of_cooling_device_register(np, 0, type, devdata, ops); - if (IS_ERR(cdev)) - return cdev; - - ret = devm_add_action_or_reset(dev, thermal_of_cooling_device_release, cdev); - if (ret) - return ERR_PTR(ret); - - return cdev; + return __devm_thermal_of_cooling_device_register(dev, np, 0, type, devdata, ops); } EXPORT_SYMBOL_GPL(devm_thermal_of_child_cooling_device_register); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index fb7649439dfa..81be6e6061b3 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -206,6 +206,11 @@ thermal_of_cooling_device_register(struct device_node *np, u32 cdev_id, const char *type, void *data, const struct thermal_cooling_device_ops *ops); +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, u32 cdev_id, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); + struct thermal_cooling_device * devm_thermal_of_child_cooling_device_register(struct device *dev, struct device_node *np, @@ -233,6 +238,14 @@ thermal_of_cooling_device_register(struct device_node *np, u32 cdev_id, return ERR_PTR(-ENODEV); } +static inline struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, u32 cdev_id, + const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return ERR_PTR(-ENODEV); +} + static inline struct thermal_cooling_device * devm_thermal_of_child_cooling_device_register(struct device *dev, struct device_node *np, -- cgit v1.2.3 From 2baee1cc03c65a35f0d053e5c5e646a3bdf6ed85 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 26 May 2026 16:08:10 +0200 Subject: thermal/of: Support cooling device ID in cooling-spec Extend the cooling device specifier parsing to support an optional cooling device identifier (cdev_id). Two formats are now supported: - Legacy format: <&cdev lower upper> - Indexed format: <&cdev cdev_id lower upper> When the indexed format is used, both the device node and the cdev_id must match in order to bind a cooling device to a thermal zone. The legacy format continues to match on the device node only, preserving backward compatibility. Update the parsing logic accordingly to handle both formats and extract the mitigation limits from the appropriate arguments. This is a preparatory step for upcoming DT bindings describing cooling devices using (device node, id) tuples instead of child nodes. No functional change for existing device trees. Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-21-daniel.lezcano@oss.qualcomm.com --- drivers/thermal/thermal_of.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 3584024b76f5..100fd8a0c8ce 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -259,16 +259,34 @@ static bool thermal_of_get_cooling_spec(struct device_node *map_np, int index, of_node_put(cooling_spec.np); - if (cooling_spec.args_count < 2) { - pr_err("wrong reference to cooling device, missing limits\n"); + /* + * There are two formats: + * - Legacy format : <&cdev lower upper> + * - New format : <&cdev cdev_id lower upper> + * + * With the new format, along with the device node pointer, + * the cdev_id must match with the cooling device cdev_id in + * order to bind + */ + if (cooling_spec.args_count < 2 || cooling_spec.args_count > 3) { + pr_err("Invalid number of cooling device parameters\n"); return false; } if (cooling_spec.np != cdev->np) return false; - c->lower = cooling_spec.args[0]; - c->upper = cooling_spec.args[1]; + if (cooling_spec.args_count == 3 && + cooling_spec.args[0] != cdev->cdev_id) + return false; + + if (cooling_spec.args_count != 3) { + c->lower = cooling_spec.args[0]; + c->upper = cooling_spec.args[1]; + } else { + c->lower = cooling_spec.args[1]; + c->upper = cooling_spec.args[2]; + } c->weight = weight; return true; -- cgit v1.2.3 From 6d0c207c0a95bc4832fe0189973f64734ef5bfc0 Mon Sep 17 00:00:00 2001 From: Gaurav Kohli Date: Tue, 26 May 2026 16:08:11 +0200 Subject: dt-bindings: thermal: cooling-devices: Update support for 3 cells cooling device Extend the thermal cooling device binding to support a 3 cells specifier along with the 2 cells format. Update #cooling-cells property to enum to support both 2 and 3 arguments. Fix pwm-fan.yaml to restrict the number of cells to 'const: 2' Signed-off-by: Gaurav Kohli Signed-off-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Reviewed-by: Krzysztof Kozlowski Reviewed-by: Lukasz Luba Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260526140802.1059293-22-daniel.lezcano@oss.qualcomm.com --- Documentation/devicetree/bindings/hwmon/pwm-fan.yaml | 3 ++- .../devicetree/bindings/thermal/thermal-cooling-devices.yaml | 8 ++++++-- Documentation/devicetree/bindings/thermal/thermal-zones.yaml | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml b/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml index a84cc3a4cfdc..6a24851fd80d 100644 --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml @@ -63,7 +63,8 @@ properties: description: The PWM that is used to control the fan. maxItems: 1 - "#cooling-cells": true + "#cooling-cells": + const: 2 required: - compatible diff --git a/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml b/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml index b9022f1613d8..28f5818f1e60 100644 --- a/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml +++ b/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml @@ -44,10 +44,14 @@ select: true properties: "#cooling-cells": description: - Must be 2, in order to specify minimum and maximum cooling state used in + Must be 2 or 3. If 2, specifies minimum and maximum cooling state used in the cooling-maps reference. The first cell is the minimum cooling state and the second cell is the maximum cooling state requested. - const: 2 + If 3, the first cell specifies the thermal mitigation device specifier + index for devices that support multiple thermal mitigation mechanisms. + The two other cells are respectively the minimum cooling state and the + maximum cooling state. + enum: [2, 3] additionalProperties: true diff --git a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml index 07d9f576ffe7..999ad40a20d5 100644 --- a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml +++ b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml @@ -211,7 +211,8 @@ patternProperties: device. Using the THERMAL_NO_LIMIT (-1UL) constant in the cooling-device phandle limit specifier lets the framework use the minimum and maximum cooling state for that cooling - device automatically. + device automatically. If three arguments are specified, + the first argument is the cooling device specifier. contribution: $ref: /schemas/types.yaml#/definitions/uint32 -- cgit v1.2.3 From 995b736ad46a80af84e58596c5e23ce94035bddf Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 1 Jun 2026 11:01:53 +0200 Subject: thermal/core: Fix missing stub for devm_thermal_cooling_device_register Even it is very unlikely the thermal framework is disabled, the newly added devm_thermal_cooling_device_register() function has not the stub when the thermal framework is optout in the kernel. Add it. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605301554.S9n45bfQ-lkp@intel.com/ Signed-off-by: Daniel Lezcano Acked-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260601090152.1243983-2-daniel.lezcano@kernel.org --- include/linux/thermal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 81be6e6061b3..083b4f533933 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -344,7 +344,11 @@ static inline struct thermal_cooling_device * thermal_cooling_device_register(const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { return ERR_PTR(-ENODEV); } + static inline struct thermal_cooling_device * +devm_thermal_cooling_device_register(struct device *dev, const char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ return ERR_PTR(-ENODEV); } static inline void thermal_cooling_device_unregister( struct thermal_cooling_device *cdev) -- cgit v1.2.3 From c665de5eeb85d1a2b87c1bb4bf4d3dbd8c1c4c37 Mon Sep 17 00:00:00 2001 From: Priyansh Jain Date: Mon, 1 Jun 2026 12:07:56 +0530 Subject: thermal/drivers/qcom/tsens: Switch wake IRQ handling to PM callbacks This change improves power management by using the standardized PM framework for wake IRQ handling. Move wake IRQ control to the PM suspend/resume path: - store uplow/critical IRQ numbers in struct tsens_priv - enable wake IRQs in tsens_suspend_common() when wakeup is allowed - disable wake IRQs in tsens_resume_common() - mark the device wakeup-capable during probe This aligns TSENS wake behavior with suspend flow and avoids keeping wake IRQs permanently enabled during runtime. Signed-off-by: Priyansh Jain Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260601-tsens_interrupt_wake_control-v2-1-ce9570946abd@oss.qualcomm.com --- drivers/thermal/qcom/tsens-v2.c | 1 - drivers/thermal/qcom/tsens.c | 64 +++++++++++++++++++++++++++++++++-------- drivers/thermal/qcom/tsens.h | 18 +++++++++++- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 8d9698ea3ec4..e06f8e5802e8 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -263,7 +263,6 @@ static int __init init_tsens_v2_no_rpm(struct tsens_priv *priv) static const struct tsens_ops ops_generic_v2 = { .init = init_common, .get_temp = get_temp_tsens_valid, - .resume = tsens_resume_common, }; struct tsens_plat_data data_tsens_v2 = { diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index cf7fc0d57a54..78d12c247fb9 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -1129,22 +1129,30 @@ static int tsens_get_temp(struct thermal_zone_device *tz, int *temp) static int __maybe_unused tsens_suspend(struct device *dev) { + int ret = 0; struct tsens_priv *priv = dev_get_drvdata(dev); - if (priv->ops && priv->ops->suspend) - return priv->ops->suspend(priv); + if (priv->ops && priv->ops->suspend) { + ret = priv->ops->suspend(priv); + if (ret) + return ret; + } - return 0; + return tsens_suspend_common(priv); } static int __maybe_unused tsens_resume(struct device *dev) { + int ret = 0; struct tsens_priv *priv = dev_get_drvdata(dev); - if (priv->ops && priv->ops->resume) - return priv->ops->resume(priv); + if (priv->ops && priv->ops->resume) { + ret = priv->ops->resume(priv); + if (ret) + return ret; + } - return 0; + return tsens_resume_common(priv); } static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); @@ -1215,7 +1223,7 @@ static const struct thermal_zone_device_ops tsens_of_ops = { }; static int tsens_register_irq(struct tsens_priv *priv, char *irqname, - irq_handler_t thread_fn) + irq_handler_t thread_fn, int *irq_num) { struct platform_device *pdev; int ret, irq; @@ -1248,7 +1256,7 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, dev_err(&pdev->dev, "%s: failed to get irq\n", __func__); else - enable_irq_wake(irq); + *irq_num = irq; } put_device(&pdev->dev); @@ -1275,11 +1283,38 @@ static int tsens_reinit(struct tsens_priv *priv) return 0; } +int tsens_suspend_common(struct tsens_priv *priv) +{ + if (!device_may_wakeup(priv->dev)) + return 0; + + if (priv->feat->combo_int) + enable_irq_wake(priv->combined_irq); + else { + enable_irq_wake(priv->uplow_irq); + if (priv->feat->crit_int) + enable_irq_wake(priv->crit_irq); + } + + return 0; +} + int tsens_resume_common(struct tsens_priv *priv) { if (pm_suspend_target_state == PM_SUSPEND_MEM) tsens_reinit(priv); + if (!device_may_wakeup(priv->dev)) + return 0; + + if (priv->feat->combo_int) + disable_irq_wake(priv->combined_irq); + else { + disable_irq_wake(priv->uplow_irq); + if (priv->feat->crit_int) + disable_irq_wake(priv->crit_irq); + } + return 0; } @@ -1319,15 +1354,18 @@ static int tsens_register(struct tsens_priv *priv) if (priv->feat->combo_int) { ret = tsens_register_irq(priv, "combined", - tsens_combined_irq_thread); + tsens_combined_irq_thread, &priv->combined_irq); } else { - ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); + ret = tsens_register_irq(priv, "uplow", tsens_irq_thread, + &priv->uplow_irq); if (ret < 0) return ret; - if (priv->feat->crit_int) + if (priv->feat->crit_int) { ret = tsens_register_irq(priv, "critical", - tsens_critical_irq_thread); + tsens_critical_irq_thread, + &priv->crit_irq); + } } return ret; @@ -1386,6 +1424,8 @@ static int tsens_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); + device_init_wakeup(dev, true); + if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) return -EINVAL; diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index ab57ad88c3f7..206ee2d5d301 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -568,6 +568,9 @@ struct tsens_context { * @ops: pointer to list of callbacks supported by this device * @debug_root: pointer to debugfs dentry for all tsens * @debug: pointer to debugfs dentry for tsens controller + * @uplow_irq: IRQ number for uplow (upper/lower) threshold interrupts + * @crit_irq: IRQ number for critical threshold interrupts + * @combined_irq: IRQ number for combined threshold interrupts * @sensor: list of sensors attached to this device */ struct tsens_priv { @@ -589,6 +592,10 @@ struct tsens_priv { struct dentry *debug_root; struct dentry *debug; + int uplow_irq; + int crit_irq; + int combined_irq; + struct tsens_sensor sensor[] __counted_by(num_sensors); }; @@ -640,8 +647,17 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp); int get_temp_common(const struct tsens_sensor *s, int *temp); #ifdef CONFIG_SUSPEND int tsens_resume_common(struct tsens_priv *priv); +int tsens_suspend_common(struct tsens_priv *priv); #else -#define tsens_resume_common NULL +static inline int tsens_resume_common(struct tsens_priv *priv) +{ + return 0; +} + +static inline int tsens_suspend_common(struct tsens_priv *priv) +{ + return 0; +} #endif /* TSENS target */ -- cgit v1.2.3 From 968098b4ca5219b0d2e0a981aed1dacfbd5adc69 Mon Sep 17 00:00:00 2001 From: Priyansh Jain Date: Mon, 1 Jun 2026 12:07:57 +0530 Subject: thermal/drivers/qcom/tsens: Disable wakeup interrupt setup on automotive targets Add a no_irq_wake flag to struct tsens_plat_data to allow platforms to control whether TSENS interrupts should be configured as wakeup sources. Create a new data_automotive structure and add compatible strings for automotive TSENS variants (SA8775P, SA8255P) with wakeup interrupts disabled. Automotive platforms can enter a low-power parking suspend state where the application processors and thermal mitigation paths are not active. In this state, waking the system due to TSENS threshold interrupts does not enable useful thermal action, but it does repeatedly break suspend residency and increase battery drain. Allow these automotive variants to keep TSENS monitoring enabled during normal runtime while opting out of TSENS wakeup interrupts during suspend, so the system can remain in low power until ignition/resume. Signed-off-by: Priyansh Jain Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20260601-tsens_interrupt_wake_control-v2-2-ce9570946abd@oss.qualcomm.com --- drivers/thermal/qcom/tsens-v2.c | 8 ++++++++ drivers/thermal/qcom/tsens.c | 8 +++++++- drivers/thermal/qcom/tsens.h | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index e06f8e5802e8..2ee117aa91ba 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -306,3 +306,11 @@ struct tsens_plat_data data_8996 = { .feat = &tsens_v2_feat, .fields = tsens_v2_regfields, }; + +/* Do not enable wakeup capable interrupts for automotive platforms */ +struct tsens_plat_data data_automotive_v2 = { + .ops = &ops_generic_v2, + .feat = &tsens_v2_feat, + .fields = tsens_v2_regfields, + .no_irq_wake = true, +}; diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index 78d12c247fb9..6e3714ecab1d 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -1212,6 +1212,12 @@ static const struct of_device_id tsens_table[] = { }, { .compatible = "qcom,tsens-v2", .data = &data_tsens_v2, + }, { + .compatible = "qcom,sa8775p-tsens", + .data = &data_automotive_v2, + }, { + .compatible = "qcom,sa8255p-tsens", + .data = &data_automotive_v2, }, {} }; @@ -1424,7 +1430,7 @@ static int tsens_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - device_init_wakeup(dev, true); + device_init_wakeup(dev, !data->no_irq_wake); if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) return -EINVAL; diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 206ee2d5d301..e8376accdff3 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -532,6 +532,7 @@ struct tsens_features { * @hw_ids: Subset of sensors ids supported by platform, if not the first n * @feat: features of the IP * @fields: bitfield locations + * @no_irq_wake: if set, TSENS interrupts will not be configured as wakeup sources */ struct tsens_plat_data { const u32 num_sensors; @@ -539,6 +540,7 @@ struct tsens_plat_data { unsigned int *hw_ids; struct tsens_features *feat; const struct reg_field *fields; + bool no_irq_wake; }; /** @@ -676,4 +678,7 @@ extern const struct tsens_plat_data data_ipq5018; extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; extern const struct tsens_plat_data data_ipq5332, data_ipq5424; +/* TSENS automotive targets */ +extern struct tsens_plat_data data_automotive_v2; + #endif /* __QCOM_TSENS_H__ */ -- cgit v1.2.3