diff options
54 files changed, 1279 insertions, 567 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/hwmon.c b/drivers/hwmon/hwmon.c index 6812d1fd7c28..c16e6da0690c 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -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"); 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/intel/int340x_thermal/platform_temperature_control.c b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c index 0ccc72c93499..d92a6f84a778 100644 --- a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c +++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c @@ -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(); diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index ccf380da12f2..bd7fd98dc310 100644 --- a/drivers/thermal/intel/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -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 = { diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c index 6c2ba0ba3178..52ea4f7fc9f9 100644 --- a/drivers/thermal/intel/intel_tcc_cooling.c +++ b/drivers/thermal/intel/intel_tcc_cooling.c @@ -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), 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/testing/command.c b/drivers/thermal/testing/command.c index 1159ecea57e7..fbf7ab9729b5 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -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; diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index 3c339242f52d..f7f9ca2f1f2c 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -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); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 2f4e2dc46b8f..28a20d4b475c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -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); - - err = -EBUSY; - if (!__find_governor(governor->name)) { - bool match_default; - - err = 0; - list_add(&governor->governor_list, &thermal_governor_list); - match_default = !strncmp(governor->name, - DEFAULT_THERMAL_GOVERNOR, - THERMAL_NAME_LENGTH); - - if (!def_governor && match_default) - 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 (__find_governor(governor->name)) + return -EBUSY; - if (!governor) - return; - - guard(mutex)(&thermal_governor_lock); - - if (!__find_governor(governor->name)) - return; + list_add(&governor->governor_list, &thermal_governor_list); - list_del(&governor->governor_list); + if (strncmp(governor->name, DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) + return 0; - guard(mutex)(&thermal_list_lock); + if (!def_governor) + def_governor = governor; - 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; + + guard(mutex)(&thermal_governor_lock); - for_each_governor_table(governor) - thermal_unregister_governor(*governor); + 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 @@ unbind: 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; - - 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) { @@ -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,7 +1798,7 @@ 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; @@ -1897,19 +1806,19 @@ static int __init thermal_init(void) 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(); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index d3acff602f9c..e98b0aa5aacc 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -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++) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index b624892bc6d6..386dfb9f559e 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -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,166 +70,97 @@ 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; - - 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 const struct attribute_group thermal_hwmon_group = { + .attrs = thermal_hwmon_attrs, + .is_visible = thermal_hwmon_attr_is_visible, +}; -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; - } + int result = PTR_ERR(hwmon->device); - 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; + 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); -void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) +static struct thermal_hwmon_device * +thermal_hwmon_lookup(const 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; + list_for_each_entry(hwmon, &thermal_hwmon_list, node) { + if (hwmon->tz == tz) + return hwmon; } + return NULL; +} - temp = thermal_hwmon_lookup_temp(hwmon, tz); - if (unlikely(!temp)) { - /* Should never happen... */ - dev_dbg(&tz->device, "temperature input lookup failed!\n"); - return; - } +void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) +{ + struct thermal_hwmon_device *hwmon; - 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); + scoped_guard(mutex, &thermal_hwmon_list_lock) { + hwmon = thermal_hwmon_lookup(tz); + if (!hwmon) + return; - 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); } - list_del(&hwmon->node); - mutex_unlock(&thermal_hwmon_list_lock); hwmon_device_unregister(hwmon->device); kfree(hwmon); 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/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 5eecae13f07d..b44abfc997ed 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -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) 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/hwmon.h b/include/linux/hwmon.h index 301a83afbd66..a578a10baff2 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -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, 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) { } |
