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

Pull thermal control updates from Rafael Wysocki:
 "These add new hardware support (i.MX93 TMU, Amlogic T7, Intel Arrow
  Lake, QCom Nord, Shikra and Hawi), fix issues in a number of places in
  the thermal control core and drivers, clean up code and refactor it in
  preparation for future changes:

   - Rework the initialization and cleanup of thermal class cooling
     devices to separate DT-based cooling device registration and
     cooling device registration without DT (Daniel Lezcano, Ovidiu
     Panait)

   - Update the cooling device DT bindings to support 3-cell cooling
     device representation, where the additional cell holds an ID to
     select a cooling mechanism for devices that offer multiple cooling
     mechanisms, and adjust the cooling device registration code
     accordingly (Gaurav Kohli, Daniel Lezcano)

   - Remove dead code from two functions in the thermal core and
     simplify the unregistration of thermal governors (Rafael Wysocki)

   - Fix critical temperature attribute removal handling in the generic
     thermal zone hwmon support code and rework that code to register a
     separate hwmon class device for each thermal zone (instead of using
     one hwmon class device for all thermal zones of the same type) to
     address thermal zone removal deadlocks (Rafael Wysocki)

   - Use attribute groups for adding temperature attributes to hwmon
     class devices associated with thermal zones (Rafael Wysocki)

   - Pass WQ_UNBOUND when allocating the thermal workqueue (Marco
     Crivellari)

   - Fix potential shift overflow in ptc_mmio_write() and improve error
     handling in proc_thermal_ptc_add() in the int340x thermal control
     driver (Aravind Anilraj)

   - Use sysfs_emit() for cpumask printing in the Intel powerclamp
     thermal driver (Yury Norov)

   - Add Arrow Lake CPU models to the intel_tcc_cooling driver (Srinivas
     Pandruvada)

   - Add QCom Nord, Shikra and Hawi temperature sensor DT bindings
     (Deepti Jaggi, Gaurav Kohli, Dipa Ramesh Mantre)

   - Use devm_add_action_or_reset() for clock disable on the NVidia
     soctherm and switch it to devm cooling device registration version
     (Daniel Lezcano)

   - Add the Amlogic T7 thermal sensor along with thermal calibration
     data read from SMC calls (Ronald Claveau)

   - Fix atomic temperature read in the QCom tsens driver to comply with
     hardware documentation (Priyansh Jain)

   - Add SpacemiT K1 thermal sensor support (Shuwei Wu)

   - Add i.MX93 temperature sensor support and filter out the invalid
     temperature (Jacky Bai)

   - Enable by default the TMU (Thermal Monitoring Unit) on Exynos
     platform (Krzysztof Kozlowski)

   - Rework interrupt initialization in the Tsens driver and add the
     optional wakeup source (Priyansh Jain)

   - Fix typo in a comment in the TSens QCom driver (Jinseok Kim)

   - Fix trailing whitespace and repeated word in the OF code, remove
     quoted string splitting across lines from the iMX7 driver, and
     remove a stray space from the thermal_trip_of_attr() macro
     definition (Mayur Kumar)

   - Update the thermal testing facility code to avoid NULL pointer
     dereferences by rejecting missing command arguments and replace
     sscanf() with kstrtoint() or kstrtoul() in that code (Ovidiu
     Panait, Samuel Moelius)"

* tag 'thermal-7.2-rc1' of gitolite.kernel.org:pub/scm/linux/kernel/git/rafael/linux-pm: (54 commits)
  thermal: sysfs: Replace sscanf() with kstrtoul()
  thermal: testing: Replace sscanf() with kstrtoint()
  thermal: testing: reject missing command arguments
  thermal: intel: intel_tcc_cooling: Add Arrow Lake CPU models
  thermal/drivers/qcom/tsens: Disable wakeup interrupt setup on automotive targets
  thermal/drivers/qcom/tsens: Switch wake IRQ handling to PM callbacks
  thermal/core: Fix missing stub for devm_thermal_cooling_device_register
  dt-bindings: thermal: cooling-devices: Update support for 3 cells cooling device
  thermal/of: Support cooling device ID in cooling-spec
  thermal/of: Pass cdev_id and introduce devm registration helper
  thermal/of: Add cooling device ID support
  thermal/of: Rename the devm_thermal_of_cooling_device_register() function
  thermal/core: Make cooling device OF node conditional on CONFIG_THERMAL_OF
  thermal/of: Move cooling device OF helpers out of thermal core
  hwmon: Use non-OF thermal cooling device registration API
  thermal/core: Add devm_thermal_cooling_device_register()
  thermal/core: Introduce non-OF thermal_cooling_device_register()
  thermal/drivers/samsung: Enable TMU by default
  thermal/driver/qoriq: Workaround unexpected temperature readings from tmu
  thermal/drivers/qoriq: Add i.MX93 tmu support
  ...
This commit is contained in:
Linus Torvalds
2026-06-15 11:35:11 +05:30
54 changed files with 1283 additions and 571 deletions

View File

@@ -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

View File

@@ -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 = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clkc_periphs CLKID_TS>;
#thermal-sensor-cells = <0>;
amlogic,secure-monitor = <&sm 1>;
};
...

View File

@@ -56,8 +56,10 @@ properties:
- enum:
- qcom,eliza-tsens
- qcom,glymur-tsens
- qcom,hawi-tsens
- qcom,kaanapali-tsens
- qcom,milos-tsens
- qcom,nord-tsens
- qcom,msm8953-tsens
- qcom,msm8996-tsens
- qcom,msm8998-tsens
@@ -74,6 +76,7 @@ properties:
- qcom,sdm630-tsens
- qcom,sdm670-tsens
- qcom,sdm845-tsens
- qcom,shikra-tsens
- qcom,sm6115-tsens
- qcom,sm6350-tsens
- qcom,sm6375-tsens

View File

@@ -25,6 +25,7 @@ properties:
enum:
- fsl,qoriq-tmu
- fsl,imx8mq-tmu
- fsl,imx93-tmu
reg:
maxItems: 1

View File

@@ -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 <shuwei.wu@mailbox.org>
$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 <dt-bindings/clock/spacemit,k1-syscon.h>
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>;
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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]);

View File

@@ -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");

View File

@@ -1082,6 +1082,7 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
* @extra_groups: pointer to list of additional non-standard attribute groups
*
* The use of this function is restricted. It is provided for legacy reasons
* and must only be called from the thermal subsystem.
@@ -1093,12 +1094,13 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
*/
struct device *
hwmon_device_register_for_thermal(struct device *dev, const char *name,
void *drvdata)
void *drvdata,
const struct attribute_group **extra_groups)
{
if (!name || !dev)
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, NULL, NULL);
return __hwmon_device_register(dev, name, drvdata, NULL, extra_groups);
}
EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, "HWMON_THERMAL");

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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",

View File

@@ -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);

View File

@@ -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.
@@ -472,6 +473,8 @@ endmenu
source "drivers/thermal/renesas/Kconfig"
source "drivers/thermal/spacemit/Kconfig"
source "drivers/thermal/tegra/Kconfig"
config GENERIC_ADC_THERMAL

View File

@@ -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

View File

@@ -25,6 +25,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/firmware/meson/meson_sm.h>
#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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -138,7 +138,7 @@ static void ptc_mmio_write(struct pci_dev *pdev, u32 offset, int index, u32 valu
reg_val = readq((void __iomem *) (proc_priv->mmio_base + offset));
reg_val &= ~mask;
reg_val |= (value << ptc_mmio_regs[index].shift);
reg_val |= ((u64)value << ptc_mmio_regs[index].shift);
writeq(reg_val, (void __iomem *) (proc_priv->mmio_base + offset));
}
@@ -278,12 +278,18 @@ static void ptc_delete_debugfs(void)
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
{
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
int i;
int i, ret;
for (i = 0; i < PTC_MAX_INSTANCES; i++) {
ptc_instance[i].offset = ptc_offsets[i];
ptc_instance[i].pdev = pdev;
ptc_create_groups(pdev, i, &ptc_instance[i]);
ret = ptc_create_groups(pdev, i, &ptc_instance[i]);
if (ret) {
while (i--)
sysfs_remove_group(&pdev->dev.kobj,
&ptc_instance[i].ptc_attr_group);
return ret;
}
}
ptc_create_debugfs();

View File

@@ -200,7 +200,7 @@ static int cpumask_get(char *buf, const struct kernel_param *kp)
if (!cpumask_available(idle_injection_cpu_mask))
return -ENODEV;
return cpumap_print_to_pagebuf(false, buf, idle_injection_cpu_mask);
return sysfs_emit(buf, "%*pb\n", cpumask_pr_args(idle_injection_cpu_mask));
}
static const struct kernel_param_ops cpumask_ops = {

View File

@@ -65,6 +65,9 @@ static const struct x86_cpu_id tcc_ids[] __initconst = {
X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL),
X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL),
X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL),
X86_MATCH_VFM(INTEL_ARROWLAKE, NULL),
X86_MATCH_VFM(INTEL_ARROWLAKE_U, NULL),
X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL),
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL),
X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL),
X86_MATCH_VFM(INTEL_NOVALAKE, NULL),

View File

@@ -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",

View File

@@ -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 = {
@@ -307,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,
};

View File

@@ -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
@@ -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;
ret = tsens_read_temp(s, temp_idx, temp);
if (!ret)
*temp = tsens_hw_to_mC(s, *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);
return 0;
return ret;
}
int get_temp_common(const struct tsens_sensor *s, int *temp)
@@ -1086,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);
@@ -1161,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,
},
{}
};
@@ -1172,7 +1229,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;
@@ -1205,7 +1262,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);
@@ -1232,11 +1289,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;
}
@@ -1276,15 +1360,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;
@@ -1343,6 +1430,8 @@ static int tsens_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
device_init_wakeup(dev, !data->no_irq_wake);
if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
return -EINVAL;

View File

@@ -21,6 +21,7 @@
#define THRESHOLD_MIN_ADC_CODE 0x0
#define MAX_SENSORS 16
#define MAX_READ_RETRY 3
#include <linux/interrupt.h>
#include <linux/thermal.h>
@@ -531,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;
@@ -538,6 +540,7 @@ struct tsens_plat_data {
unsigned int *hw_ids;
struct tsens_features *feat;
const struct reg_field *fields;
bool no_irq_wake;
};
/**
@@ -567,6 +570,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 {
@@ -588,6 +594,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);
};
@@ -639,8 +649,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 */
@@ -659,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__ */

View File

@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2016 Freescale Semiconductor, Inc.
// Copyright 2025 NXP
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -24,10 +26,14 @@
#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
/* errata ID info define */
#define TMU_ERR052243 BIT(0)
#define REGS_TMR 0x000 /* Mode Register */
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
@@ -43,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 */
@@ -73,14 +88,26 @@ struct qoriq_sensor {
int id;
};
struct tmu_drvdata {
u32 teumr0;
u32 tmu_errata;
};
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 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]);
@@ -90,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:
*
@@ -123,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 {
@@ -234,9 +270,18 @@ 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);
}
/* 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);
}
@@ -319,6 +364,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 +425,23 @@ 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,
.tmu_errata = TMU_ERR052243,
};
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);

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SPACEMIT_K1_TSENSOR) += k1_tsensor.o

View File

@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Thermal sensor driver for SpacemiT K1 SoC
*
* Copyright (C) 2026 Shuwei Wu <shuwei.wu@mailbox.org>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#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 <shuwei.wu@mailbox.org>");
MODULE_LICENSE("GPL");

View File

@@ -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)
{
@@ -1700,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_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",
@@ -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)

View File

@@ -116,18 +116,30 @@ static int tt_command_exec(int index, const char *arg)
break;
case TT_CMD_DELTZ:
if (!arg || !*arg)
return -EINVAL;
ret = tt_del_tz(arg);
break;
case TT_CMD_TZADDTRIP:
if (!arg || !*arg)
return -EINVAL;
ret = tt_zone_add_trip(arg);
break;
case TT_CMD_TZREG:
if (!arg || !*arg)
return -EINVAL;
ret = tt_zone_reg(arg);
break;
case TT_CMD_TZUNREG:
if (!arg || !*arg)
return -EINVAL;
ret = tt_zone_unreg(arg);
break;

View File

@@ -239,9 +239,9 @@ int tt_del_tz(const char *arg)
int ret;
int id;
ret = sscanf(arg, "%d", &id);
if (ret != 1)
return -EINVAL;
ret = kstrtoint(arg, 10, &id);
if (ret < 0)
return ret;
struct tt_work *tt_work __free(kfree) = kzalloc_obj(*tt_work);
if (!tt_work)
@@ -279,9 +279,9 @@ static struct tt_thermal_zone *tt_get_tt_zone(const char *arg)
struct tt_thermal_zone *tt_zone;
int ret, id;
ret = sscanf(arg, "%d", &id);
if (ret != 1)
return ERR_PTR(-EINVAL);
ret = kstrtoint(arg, 10, &id);
if (ret < 0)
return ERR_PTR(ret);
guard(mutex)(&tt_thermal_zones_lock);

View File

@@ -116,78 +116,24 @@ static int thermal_set_governor(struct thermal_zone_device *tz,
return ret;
}
int thermal_register_governor(struct thermal_governor *governor)
static int __init thermal_register_governor(struct thermal_governor *governor)
{
int err;
const char *name;
struct thermal_zone_device *pos;
if (!governor)
return -EINVAL;
guard(mutex)(&thermal_governor_lock);
if (__find_governor(governor->name))
return -EBUSY;
err = -EBUSY;
if (!__find_governor(governor->name)) {
bool match_default;
list_add(&governor->governor_list, &thermal_governor_list);
err = 0;
list_add(&governor->governor_list, &thermal_governor_list);
match_default = !strncmp(governor->name,
DEFAULT_THERMAL_GOVERNOR,
THERMAL_NAME_LENGTH);
if (strncmp(governor->name, DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
return 0;
if (!def_governor && match_default)
def_governor = governor;
}
if (!def_governor)
def_governor = governor;
guard(mutex)(&thermal_list_lock);
list_for_each_entry(pos, &thermal_tz_list, node) {
/*
* only thermal zones with specified tz->tzp->governor_name
* may run with tz->govenor unset
*/
if (pos->governor)
continue;
name = pos->tzp->governor_name;
if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
int ret;
ret = thermal_set_governor(pos, governor);
if (ret)
dev_err(&pos->device,
"Failed to set governor %s for thermal zone %s: %d\n",
governor->name, pos->type, ret);
}
}
return err;
}
void thermal_unregister_governor(struct thermal_governor *governor)
{
struct thermal_zone_device *pos;
if (!governor)
return;
guard(mutex)(&thermal_governor_lock);
if (!__find_governor(governor->name))
return;
list_del(&governor->governor_list);
guard(mutex)(&thermal_list_lock);
list_for_each_entry(pos, &thermal_tz_list, node) {
if (!strncasecmp(pos->governor->name, governor->name,
THERMAL_NAME_LENGTH))
thermal_set_governor(pos, NULL);
}
return 0;
}
int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
@@ -225,40 +171,34 @@ int thermal_build_list_of_policies(char *buf)
static void __init thermal_unregister_governors(void)
{
struct thermal_governor **governor;
struct thermal_governor *gov, *pos;
for_each_governor_table(governor)
thermal_unregister_governor(*governor);
guard(mutex)(&thermal_governor_lock);
list_for_each_entry_safe(gov, pos, &thermal_governor_list, governor_list)
list_del(&gov->governor_list);
}
static int __init thermal_register_governors(void)
{
int ret = 0;
struct thermal_governor **governor;
guard(mutex)(&thermal_governor_lock);
for_each_governor_table(governor) {
int ret;
ret = thermal_register_governor(*governor);
if (ret) {
pr_err("Failed to register governor: '%s'",
(*governor)->name);
break;
return ret;
}
pr_info("Registered thermal governor '%s'",
(*governor)->name);
pr_info("Registered thermal governor '%s'", (*governor)->name);
}
if (ret) {
struct thermal_governor **gov;
for_each_governor_table(gov) {
if (gov == governor)
break;
thermal_unregister_governor(*gov);
}
}
return ret;
return 0;
}
static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz,
@@ -949,34 +889,7 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
kfree(pos);
}
static void thermal_release(struct device *dev)
{
struct thermal_zone_device *tz;
struct thermal_cooling_device *cdev;
if (!strncmp(dev_name(dev), "thermal_zone",
sizeof("thermal_zone") - 1)) {
tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz);
thermal_set_governor(tz, NULL);
ida_destroy(&tz->ida);
mutex_destroy(&tz->lock);
complete(&tz->removal);
} else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) {
cdev = to_cooling_device(dev);
thermal_cooling_device_destroy_sysfs(cdev);
kfree_const(cdev->type);
ida_free(&thermal_cdev_ida, cdev->id);
kfree(cdev);
}
}
static const struct class thermal_class = {
.name = "thermal",
.dev_release = thermal_release,
};
static bool thermal_class_unavailable __ro_after_init = true;
static struct class *thermal_class __ro_after_init;
static inline
void print_bind_err_msg(struct thermal_zone_device *tz,
@@ -1040,42 +953,35 @@ static void thermal_cooling_device_init_complete(struct thermal_cooling_device *
thermal_zone_cdev_bind(tz, cdev);
}
/**
* __thermal_cooling_device_register() - register a new 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.
*/
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)
static void thermal_cdev_release(struct device *dev)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
thermal_cooling_device_destroy_sysfs(cdev);
kfree_const(cdev->type);
ida_free(&thermal_cdev_ida, cdev->id);
kfree(cdev);
}
struct thermal_cooling_device *
thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops)
{
struct thermal_cooling_device *cdev;
unsigned long current_state;
int ret;
if (!ops || !ops->get_max_state || !ops->get_cur_state ||
!ops->set_cur_state)
return ERR_PTR(-EINVAL);
if (thermal_class_unavailable)
if (!thermal_class)
return ERR_PTR(-ENODEV);
cdev = kzalloc_obj(*cdev);
if (!cdev)
return ERR_PTR(-ENOMEM);
cdev->ops = ops;
ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL);
if (ret < 0)
goto out_kfree_cdev;
@@ -1087,17 +993,35 @@ __thermal_cooling_device_register(struct device_node *np,
goto out_ida_remove;
}
return cdev;
out_ida_remove:
ida_free(&thermal_cdev_ida, cdev->id);
out_kfree_cdev:
kfree(cdev);
return ERR_PTR(ret);
}
int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata)
{
unsigned long current_state;
int ret;
mutex_init(&cdev->lock);
INIT_LIST_HEAD(&cdev->thermal_instances);
cdev->np = np;
cdev->ops = ops;
cdev->updated = false;
cdev->device.class = &thermal_class;
cdev->device.class = thermal_class;
cdev->device.release = thermal_cdev_release;
device_initialize(&cdev->device);
cdev->devdata = devdata;
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
if (ret)
goto out_put_device;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
if (ret)
goto out_cdev_type;
goto out_put_device;
/*
* The cooling device's current state is only needed for debug
@@ -1113,40 +1037,32 @@ __thermal_cooling_device_register(struct device_node *np,
thermal_cooling_device_setup_sysfs(cdev);
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
ret = device_add(&cdev->device);
if (ret)
goto out_cooling_dev;
ret = device_register(&cdev->device);
if (ret) {
/* thermal_release() handles rest of the cleanup */
put_device(&cdev->device);
return ERR_PTR(ret);
}
goto out_put_device;
if (current_state <= cdev->max_state)
thermal_debug_cdev_add(cdev, current_state);
thermal_cooling_device_init_complete(cdev);
return cdev;
return 0;
out_cooling_dev:
thermal_cooling_device_destroy_sysfs(cdev);
out_cdev_type:
kfree_const(cdev->type);
out_ida_remove:
ida_free(&thermal_cdev_ida, cdev->id);
out_kfree_cdev:
kfree(cdev);
return ERR_PTR(ret);
out_put_device:
/*
* The device core will release the memory via
* thermal_release() after put_device() is called in the error
* path
*/
put_device(&cdev->device);
return ret;
}
/**
* 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.
* @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
@@ -1159,82 +1075,62 @@ 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);
struct thermal_cooling_device *cdev;
int ret;
cdev = thermal_cooling_device_alloc(type, ops);
if (IS_ERR(cdev))
return cdev;
ret = thermal_cooling_device_add(cdev, devdata);
if (ret)
return ERR_PTR(ret);
return cdev;
}
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 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 *
thermal_of_cooling_device_register(struct device_node *np,
const char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
static void thermal_cooling_device_release(void *data)
{
return __thermal_cooling_device_register(np, type, devdata, ops);
}
EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
struct thermal_cooling_device *cdev = data;
static void thermal_cooling_device_release(struct device *dev, void *res)
{
thermal_cooling_device_unregister(
*(struct thermal_cooling_device **)res);
thermal_cooling_device_unregister(cdev);
}
/**
* devm_thermal_of_cooling_device_register() - register an OF thermal cooling
* device
* devm_thermal_cooling_device_register() - register a 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.
* 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_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_cooling_device_register(struct device *dev, const char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
{
struct thermal_cooling_device **ptr, *tcd;
struct thermal_cooling_device *cdev;
int ret;
ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
cdev = thermal_cooling_device_register(type, devdata, ops);
if (IS_ERR(cdev))
return cdev;
tcd = __thermal_cooling_device_register(np, type, devdata, ops);
if (IS_ERR(tcd)) {
devres_free(ptr);
return tcd;
}
ret = devm_add_action_or_reset(dev, thermal_cooling_device_release, cdev);
if (ret)
return ERR_PTR(ret);
*ptr = tcd;
devres_add(dev, ptr);
return tcd;
return cdev;
}
EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register);
EXPORT_SYMBOL_GPL(devm_thermal_cooling_device_register);
static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev)
{
@@ -1470,6 +1366,17 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz)
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
static void thermal_zone_device_release(struct device *dev)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz);
thermal_set_governor(tz, NULL);
ida_destroy(&tz->ida);
mutex_destroy(&tz->lock);
complete(&tz->removal);
}
/**
* thermal_zone_device_register_with_trips() - register a new thermal zone device
* @type: the thermal zone device type
@@ -1540,7 +1447,7 @@ thermal_zone_device_register_with_trips(const char *type,
if (polling_delay && passive_delay > polling_delay)
return ERR_PTR(-EINVAL);
if (thermal_class_unavailable)
if (!thermal_class)
return ERR_PTR(-ENODEV);
tz = kzalloc_flex(*tz, trips, num_trips);
@@ -1576,7 +1483,8 @@ thermal_zone_device_register_with_trips(const char *type,
if (!tz->ops.critical)
tz->ops.critical = thermal_zone_device_critical;
tz->device.class = &thermal_class;
tz->device.class = thermal_class;
tz->device.release = thermal_zone_device_release;
tz->devdata = devdata;
tz->num_trips = num_trips;
for_each_trip_desc(tz, td) {
@@ -1837,7 +1745,7 @@ static void __thermal_pm_prepare(void)
void thermal_pm_prepare(void)
{
if (thermal_class_unavailable)
if (!thermal_class)
return;
__thermal_pm_prepare();
@@ -1868,7 +1776,7 @@ void thermal_pm_complete(void)
{
struct thermal_zone_device *tz;
if (thermal_class_unavailable)
if (!thermal_class)
return;
guard(mutex)(&thermal_list_lock);
@@ -1881,6 +1789,7 @@ void thermal_pm_complete(void)
static int __init thermal_init(void)
{
struct class *tc;
int result;
thermal_debug_init();
@@ -1889,27 +1798,27 @@ static int __init thermal_init(void)
if (result)
goto error;
thermal_wq = alloc_workqueue("thermal_events", WQ_POWER_EFFICIENT, 0);
thermal_wq = alloc_workqueue("thermal_events", WQ_UNBOUND, 0);
if (!thermal_wq) {
result = -ENOMEM;
goto unregister_netlink;
}
result = thermal_register_governors();
if (result)
goto destroy_workqueue;
result = class_register(&thermal_class);
if (result)
goto unregister_governors;
thermal_class_unavailable = false;
tc = class_create("thermal");
if (IS_ERR(tc)) {
result = PTR_ERR(tc);
goto unregister_governors;
}
thermal_class = tc;
return 0;
unregister_governors:
thermal_unregister_governors();
destroy_workqueue:
destroy_workqueue(thermal_wq);
unregister_netlink:
thermal_netlink_exit();

View File

@@ -258,8 +258,6 @@ struct thermal_instance {
#define to_cooling_device(_dev) \
container_of(_dev, struct thermal_cooling_device, device)
int thermal_register_governor(struct thermal_governor *);
void thermal_unregister_governor(struct thermal_governor *);
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
int thermal_build_list_of_policies(char *buf);
void __thermal_zone_device_update(struct thermal_zone_device *tz,
@@ -269,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++)

View File

@@ -19,27 +19,19 @@
#include "thermal_hwmon.h"
#include "thermal_core.h"
/*
* Needs to be large enough to hold a thermal zone type string followed by an
* underline character and a 32-bit integer in decimal representation.
*/
#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11)
/* hwmon sys I/F */
/* thermal zone devices with the same type share one hwmon device */
struct thermal_hwmon_device {
char type[THERMAL_NAME_LENGTH];
char name[THERMAL_HWMON_NAME_LENGTH];
struct device *device;
int count;
struct list_head tz_list;
struct list_head node;
};
struct thermal_hwmon_attr {
struct device_attribute attr;
char name[16];
};
/* one temperature input for each thermal zone */
struct thermal_hwmon_temp {
struct list_head hwmon_node;
struct thermal_zone_device *tz;
struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
};
static LIST_HEAD(thermal_hwmon_list);
@@ -47,19 +39,14 @@ static LIST_HEAD(thermal_hwmon_list);
static DEFINE_MUTEX(thermal_hwmon_list_lock);
static ssize_t
temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
struct thermal_zone_device *tz = hwmon->tz;
int temperature;
int ret;
struct thermal_hwmon_attr *hwmon_attr
= container_of(attr, struct thermal_hwmon_attr, attr);
struct thermal_hwmon_temp *temp
= container_of(hwmon_attr, struct thermal_hwmon_temp,
temp_input);
struct thermal_zone_device *tz = temp->tz;
ret = thermal_zone_get_temp(tz, &temperature);
if (ret)
return ret;
@@ -67,14 +54,10 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_hwmon_attr *hwmon_attr
= container_of(attr, struct thermal_hwmon_attr, attr);
struct thermal_hwmon_temp *temp
= container_of(hwmon_attr, struct thermal_hwmon_temp,
temp_crit);
struct thermal_zone_device *tz = temp->tz;
struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
struct thermal_zone_device *tz = hwmon->tz;
int temperature;
int ret;
@@ -87,167 +70,98 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
return sysfs_emit(buf, "%d\n", temperature);
}
static DEVICE_ATTR_RO(temp1_input);
static DEVICE_ATTR_RO(temp1_crit);
static struct thermal_hwmon_device *
thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
static struct attribute *thermal_hwmon_attrs[] = {
&dev_attr_temp1_input.attr,
&dev_attr_temp1_crit.attr,
NULL,
};
static umode_t thermal_hwmon_attr_is_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct thermal_hwmon_device *hwmon;
char type[THERMAL_NAME_LENGTH];
if (a == &dev_attr_temp1_input.attr)
return a->mode;
mutex_lock(&thermal_hwmon_list_lock);
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
strscpy(type, tz->type);
strreplace(type, '-', '_');
if (!strcmp(hwmon->type, type)) {
mutex_unlock(&thermal_hwmon_list_lock);
return hwmon;
}
if (a == &dev_attr_temp1_crit.attr) {
struct thermal_hwmon_device *hwmon = dev_get_drvdata(kobj_to_dev(kobj));
struct thermal_zone_device *tz = hwmon->tz;
int dummy;
if (tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &dummy))
return a->mode;
}
mutex_unlock(&thermal_hwmon_list_lock);
return NULL;
return 0;
}
/* Find the temperature input matching a given thermal zone */
static struct thermal_hwmon_temp *
thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
const struct thermal_zone_device *tz)
{
struct thermal_hwmon_temp *temp;
static const struct attribute_group thermal_hwmon_group = {
.attrs = thermal_hwmon_attrs,
.is_visible = thermal_hwmon_attr_is_visible,
};
mutex_lock(&thermal_hwmon_list_lock);
list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
if (temp->tz == tz) {
mutex_unlock(&thermal_hwmon_list_lock);
return temp;
}
mutex_unlock(&thermal_hwmon_list_lock);
return NULL;
}
static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
{
int temp;
return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp);
}
__ATTRIBUTE_GROUPS(thermal_hwmon);
int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
struct thermal_hwmon_temp *temp;
int new_hwmon_device = 1;
int result;
hwmon = thermal_hwmon_lookup_by_type(tz);
if (hwmon) {
new_hwmon_device = 0;
goto register_sys_interface;
}
hwmon = kzalloc_obj(*hwmon);
if (!hwmon)
return -ENOMEM;
INIT_LIST_HEAD(&hwmon->tz_list);
strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
strreplace(hwmon->type, '-', '_');
hwmon->tz = tz;
/*
* Append the thermal zone ID preceded by an underline character to the
* type to disambiguate the sensors command output.
*/
scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id);
strreplace(hwmon->name, '-', '_');
hwmon->device = hwmon_device_register_for_thermal(&tz->device,
hwmon->type, hwmon);
hwmon->name, hwmon,
thermal_hwmon_groups);
if (IS_ERR(hwmon->device)) {
result = PTR_ERR(hwmon->device);
goto free_mem;
}
register_sys_interface:
temp = kzalloc_obj(*temp);
if (!temp) {
result = -ENOMEM;
goto unregister_name;
}
temp->tz = tz;
hwmon->count++;
snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
"temp%d_input", hwmon->count);
temp->temp_input.attr.attr.name = temp->temp_input.name;
temp->temp_input.attr.attr.mode = 0444;
temp->temp_input.attr.show = temp_input_show;
sysfs_attr_init(&temp->temp_input.attr.attr);
result = device_create_file(hwmon->device, &temp->temp_input.attr);
if (result)
goto free_temp_mem;
if (thermal_zone_crit_temp_valid(tz)) {
snprintf(temp->temp_crit.name,
sizeof(temp->temp_crit.name),
"temp%d_crit", hwmon->count);
temp->temp_crit.attr.attr.name = temp->temp_crit.name;
temp->temp_crit.attr.attr.mode = 0444;
temp->temp_crit.attr.show = temp_crit_show;
sysfs_attr_init(&temp->temp_crit.attr.attr);
result = device_create_file(hwmon->device,
&temp->temp_crit.attr);
if (result)
goto unregister_input;
int result = PTR_ERR(hwmon->device);
kfree(hwmon);
return result;
}
/* The list is needed for hwmon lookup during removal. */
mutex_lock(&thermal_hwmon_list_lock);
if (new_hwmon_device)
list_add_tail(&hwmon->node, &thermal_hwmon_list);
list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
list_add_tail(&hwmon->node, &thermal_hwmon_list);
mutex_unlock(&thermal_hwmon_list_lock);
return 0;
unregister_input:
device_remove_file(hwmon->device, &temp->temp_input.attr);
free_temp_mem:
kfree(temp);
unregister_name:
if (new_hwmon_device)
hwmon_device_unregister(hwmon->device);
free_mem:
kfree(hwmon);
return result;
}
EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
static struct thermal_hwmon_device *
thermal_hwmon_lookup(const struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
if (hwmon->tz == tz)
return hwmon;
}
return NULL;
}
void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
struct thermal_hwmon_temp *temp;
hwmon = thermal_hwmon_lookup_by_type(tz);
if (unlikely(!hwmon)) {
/* Should never happen... */
dev_dbg(&tz->device, "hwmon device lookup failed!\n");
return;
scoped_guard(mutex, &thermal_hwmon_list_lock) {
hwmon = thermal_hwmon_lookup(tz);
if (!hwmon)
return;
list_del(&hwmon->node);
}
temp = thermal_hwmon_lookup_temp(hwmon, tz);
if (unlikely(!temp)) {
/* Should never happen... */
dev_dbg(&tz->device, "temperature input lookup failed!\n");
return;
}
device_remove_file(hwmon->device, &temp->temp_input.attr);
if (thermal_zone_crit_temp_valid(tz))
device_remove_file(hwmon->device, &temp->temp_crit.attr);
mutex_lock(&thermal_hwmon_list_lock);
list_del(&temp->hwmon_node);
kfree(temp);
if (!list_empty(&hwmon->tz_list)) {
mutex_unlock(&thermal_hwmon_list_lock);
return;
}
list_del(&hwmon->node);
mutex_unlock(&thermal_hwmon_list_lock);
hwmon_device_unregister(hwmon->device);
kfree(hwmon);
}

View File

@@ -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;
@@ -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;
@@ -494,7 +512,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
@@ -510,3 +528,125 @@ 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.
* @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.
*
* 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, u32 cdev_id,
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;
cdev->cdev_id = cdev_id;
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);
}
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
* @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.
*
* 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_child_cooling_device_register(struct device *dev,
struct device_node *np,
const char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
{
return __devm_thermal_of_cooling_device_register(dev, np, 0, type, devdata, ops);
}
EXPORT_SYMBOL_GPL(devm_thermal_of_child_cooling_device_register);

View File

@@ -82,7 +82,7 @@ mode_store(struct device *dev, struct device_attribute *attr,
}
#define thermal_trip_of_attr(_ptr_, _attr_) \
({ \
({ \
struct thermal_trip_desc *td; \
\
td = container_of(_ptr_, struct thermal_trip_desc, \
@@ -536,11 +536,9 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
unsigned long state;
int result;
if (sscanf(buf, "%ld\n", &state) != 1)
return -EINVAL;
if ((long)state < 0)
return -EINVAL;
result = kstrtoul(buf, 10, &state);
if (result < 0)
return result;
/* Requested state should be less than max_state + 1 */
if (state > cdev->max_state)

View File

@@ -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_ */

View File

@@ -477,7 +477,8 @@ hwmon_device_register_with_info(struct device *dev,
const struct attribute_group **extra_groups);
struct device *
hwmon_device_register_for_thermal(struct device *dev, const char *name,
void *drvdata);
void *drvdata,
const struct attribute_group **extra_groups);
struct device *
devm_hwmon_device_register_with_info(struct device *dev,
const char *name, void *drvdata,

View File

@@ -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,10 @@ 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;
u32 cdev_id;
#endif
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
@@ -198,6 +201,21 @@ 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, 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,
const char *type, void *devdata,
const struct thermal_cooling_device_ops *ops);
#else
static inline
@@ -211,6 +229,31 @@ 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, 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_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,
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,14 +295,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 *);
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_cooling_device_register(struct device *dev, 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);
@@ -304,19 +344,12 @@ 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 *
thermal_of_cooling_device_register(struct device_node *np,
const char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
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 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)
{ }