diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-06-08 15:00:20 +0200 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-06-08 15:00:20 +0200 |
| commit | bdbca04e7e33b8c8051402b173922a6fdc74bf92 (patch) | |
| tree | 06f7a1d68ec68edfed5dc4848b31b6530782502a | |
| parent | 21c315342b81526874acfa311f11b3f72bed4e14 (diff) | |
| parent | 968098b4ca5219b0d2e0a981aed1dacfbd5adc69 (diff) | |
Merge tag 'thermal-v7.2-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/thermal/linux
Pull thermal driver updates for 7.2 from Daniel Lezcano:
- Add the QCom Nord temperature sensor DT bindings (Deepti Jaggi)
- Use devm_add_action_or_reset() for clock disable on the NVidia
soctherm and switch to devm cooling device registration version
(Daniel Lezcano)
- Replace the devm version implementation by the helper doing the same
thing (Daniel Lezcano)
- Add the Amlogic T7 thermal sensor along with thermal calibration
data read from SMC calls (Ronald Claveau)
- Fix typo in comment, "uppper" with "upper" in the TSens QCom driver
(Jinseok Kim)
- Add the QCom Shikra temperature sensor DT bindings (Gaurav Kohli)
- Add the QCom Hawi temperature sensor DT bindings (Dipa Ramesh Mantre)
- Fix atomic temperature read in the QCom tsens to comply with
hardware documentation (Priyansh Jain)
- Fix trailing whitespace and repeated word in the OF code. Do not
split quoted string across lines in the iMX7 driver (Mayur Kumar)
- Add SpacemiT K1 thermal sensor support (Shuwei Wu)
- Add the 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)
- Split the core code and the OF which are interleaved. Add the
cooling device per index registration in order to support dedicated
cooling devices controller (Daniel Lezcano)
- Add DT binding to specify an index in the cooling device map (Gaurav
Kohli)
- Rework interrupt initialization in the Tsens driver and add the
optional wakeup source (Priyansh Jain)"
* tag 'thermal-v7.2-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (34 commits)
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
dt-bindings: thermal: qoriq: Add compatible string for imx93
thermal/drivers/spacemit/k1: Add thermal sensor support
dt-bindings: thermal: Add SpacemiT K1 thermal sensor
thermal/drivers/imx: Do not split quoted string across lines
...
45 files changed, 1070 insertions, 258 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/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 = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc_periphs CLKID_TS>; + #thermal-sensor-cells = <0>; + amlogic,secure-monitor = <&sm 1>; + }; ... diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml index 7d34ba00e684..f0efaa8349ee 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml @@ -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 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 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 <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>; + }; 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 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 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/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/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/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/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); 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/Kconfig b/drivers/thermal/Kconfig index b10080d61860..810eeccedfba 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. @@ -472,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/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 <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; 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/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); 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/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 8d9698ea3ec4..2ee117aa91ba 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 = { @@ -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, +}; diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index a2422ebee816..6e3714ecab1d 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 @@ -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) @@ -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; diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 2a7afa4c899b..e8376accdff3 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 <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__ */ diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 01b58be0dcc6..35439ec5f8bc 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -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); 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 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 <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"); diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 5d26b52beaba..d8e988a0d43e 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) { @@ -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) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 7d7ce855ae88..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; @@ -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,108 +1082,55 @@ __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); -/** - * 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; - - 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(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); +EXPORT_SYMBOL_GPL(devm_thermal_cooling_device_register); static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) { 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 99085c806a1f..100fd8a0c8ce 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; @@ -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); 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_ */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 0ddc77aeeca2..083b4f533933 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,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) { } |
