mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Merge back earlier thermal control material for 7.2
This commit is contained in:
commit
9e2d7e32da
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
...
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ properties:
|
|||
enum:
|
||||
- fsl,qoriq-tmu
|
||||
- fsl,imx8mq-tmu
|
||||
- fsl,imx93-tmu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
* @up_viol: upper threshold violated
|
||||
* @up_thresh: upper threshold temperature value
|
||||
* @up_irq_mask: mask register for upper threshold irqs
|
||||
* @up_irq_clear: clear register for uppper threshold irqs
|
||||
* @up_irq_clear: clear register for upper threshold irqs
|
||||
* @low_viol: lower threshold violated
|
||||
* @low_thresh: lower threshold temperature value
|
||||
* @low_irq_mask: mask register for lower threshold irqs
|
||||
|
|
@ -316,9 +316,66 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
|
|||
}
|
||||
|
||||
/**
|
||||
* tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
|
||||
* tsens_read_temp - Retrieve temperature readings from the hardware.
|
||||
* @s: Pointer to sensor struct
|
||||
* @field: Index into regmap_field array pointing to temperature data
|
||||
* @temp: temperature in deciCelsius to be read from hardware
|
||||
*
|
||||
* This function handles temperature returned in ADC code or deciCelsius
|
||||
* depending on IP version.
|
||||
*
|
||||
* Return: 0 on success, a negative errno will be returned in error cases
|
||||
*/
|
||||
static int tsens_read_temp(const struct tsens_sensor *s, int field, int *temp)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
int temp_val[MAX_READ_RETRY] = {0};
|
||||
u32 status;
|
||||
int ret;
|
||||
u32 last_temp_mask = GENMASK(priv->fields[LAST_TEMP_0].msb,
|
||||
priv->fields[LAST_TEMP_0].lsb);
|
||||
u32 valid_bit = priv->rf[VALID_0] ? BIT(priv->fields[VALID_0].lsb) : 0;
|
||||
|
||||
for (int i = 0; i < MAX_READ_RETRY; i++) {
|
||||
ret = regmap_read(priv->tm_map, priv->fields[field].reg, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* VER_0 doesn't have a VALID bit */
|
||||
if (!valid_bit) {
|
||||
*temp = status & last_temp_mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
temp_val[i] = status & last_temp_mask;
|
||||
|
||||
if (status & valid_bit) {
|
||||
*temp = temp_val[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* As per the HW guidelines, if none of the attempts observe a
|
||||
* valid sample, a stable fallback value must be returned. If the
|
||||
* first and second samples match, the second value is returned;
|
||||
* otherwise, if the second and third samples match, the third
|
||||
* value is returned.
|
||||
*/
|
||||
if (temp_val[0] == temp_val[1])
|
||||
*temp = temp_val[1];
|
||||
else if (temp_val[1] == temp_val[2])
|
||||
*temp = temp_val[2];
|
||||
else
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
|
||||
* @s: Pointer to sensor struct
|
||||
* @temp: temperature in milliCelsius to be read from hardware
|
||||
*
|
||||
* This function handles temperature returned in ADC code or deciCelsius
|
||||
* depending on IP version.
|
||||
|
|
@ -326,20 +383,14 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
|
|||
* Return: Temperature in milliCelsius on success, a negative errno will
|
||||
* be returned in error cases
|
||||
*/
|
||||
static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
|
||||
static int tsens_hw_to_mC(const struct tsens_sensor *s, int temp)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
u32 resolution;
|
||||
u32 temp = 0;
|
||||
int ret;
|
||||
|
||||
resolution = priv->fields[LAST_TEMP_0].msb -
|
||||
priv->fields[LAST_TEMP_0].lsb;
|
||||
|
||||
ret = regmap_field_read(priv->rf[field], &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Convert temperature from ADC code to milliCelsius */
|
||||
if (priv->feat->adc)
|
||||
return code_to_degc(temp, s) * 1000;
|
||||
|
|
@ -514,8 +565,10 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
|
|||
&d->crit_irq_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
|
||||
ret = regmap_field_read(priv->rf[CRIT_THRESH_0 + hw_id], &d->crit_thresh);
|
||||
if (ret)
|
||||
return ret;
|
||||
d->crit_thresh = tsens_hw_to_mC(s, d->crit_thresh);
|
||||
} else {
|
||||
/* No mask register on older TSENS */
|
||||
d->up_irq_mask = 0;
|
||||
|
|
@ -525,8 +578,16 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
|
|||
d->crit_thresh = 0;
|
||||
}
|
||||
|
||||
d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
|
||||
d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
|
||||
ret = regmap_field_read(priv->rf[UP_THRESH_0 + hw_id], &d->up_thresh);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
d->up_thresh = tsens_hw_to_mC(s, d->up_thresh);
|
||||
ret = regmap_field_read(priv->rf[LOW_THRESH_0 + hw_id], &d->low_thresh);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
d->low_thresh = tsens_hw_to_mC(s, d->low_thresh);
|
||||
|
||||
dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
|
||||
hw_id, __func__,
|
||||
|
|
@ -750,33 +811,15 @@ static void tsens_disable_irq(struct tsens_priv *priv)
|
|||
|
||||
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
int hw_id = s->hw_id;
|
||||
u32 temp_idx = LAST_TEMP_0 + hw_id;
|
||||
u32 valid_idx = VALID_0 + hw_id;
|
||||
u32 valid;
|
||||
int ret;
|
||||
|
||||
/* VER_0 doesn't have VALID bit */
|
||||
if (tsens_version(priv) == VER_0)
|
||||
goto get_temp;
|
||||
ret = tsens_read_temp(s, temp_idx, temp);
|
||||
if (!ret)
|
||||
*temp = tsens_hw_to_mC(s, *temp);
|
||||
|
||||
/* Valid bit is 0 for 6 AHB clock cycles.
|
||||
* At 19.2MHz, 1 AHB clock is ~60ns.
|
||||
* We should enter this loop very, very rarely.
|
||||
* Wait 1 us since it's the min of poll_timeout macro.
|
||||
* Old value was 400 ns.
|
||||
*/
|
||||
ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid,
|
||||
valid, 1, 20 * USEC_PER_MSEC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
get_temp:
|
||||
/* Valid bit is set, OK to read the temperature */
|
||||
*temp = tsens_hw_to_mC(s, temp_idx);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_temp_common(const struct tsens_sensor *s, int *temp)
|
||||
|
|
@ -1086,22 +1129,30 @@ static int tsens_get_temp(struct thermal_zone_device *tz, int *temp)
|
|||
|
||||
static int __maybe_unused tsens_suspend(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tsens_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->ops && priv->ops->suspend)
|
||||
return priv->ops->suspend(priv);
|
||||
if (priv->ops && priv->ops->suspend) {
|
||||
ret = priv->ops->suspend(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return tsens_suspend_common(priv);
|
||||
}
|
||||
|
||||
static int __maybe_unused tsens_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tsens_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->ops && priv->ops->resume)
|
||||
return priv->ops->resume(priv);
|
||||
if (priv->ops && priv->ops->resume) {
|
||||
ret = priv->ops->resume(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return tsens_resume_common(priv);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
|
||||
|
|
@ -1161,6 +1212,12 @@ static const struct of_device_id tsens_table[] = {
|
|||
}, {
|
||||
.compatible = "qcom,tsens-v2",
|
||||
.data = &data_tsens_v2,
|
||||
}, {
|
||||
.compatible = "qcom,sa8775p-tsens",
|
||||
.data = &data_automotive_v2,
|
||||
}, {
|
||||
.compatible = "qcom,sa8255p-tsens",
|
||||
.data = &data_automotive_v2,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
|
@ -1172,7 +1229,7 @@ static const struct thermal_zone_device_ops tsens_of_ops = {
|
|||
};
|
||||
|
||||
static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
|
||||
irq_handler_t thread_fn)
|
||||
irq_handler_t thread_fn, int *irq_num)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int ret, irq;
|
||||
|
|
@ -1205,7 +1262,7 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
|
|||
dev_err(&pdev->dev, "%s: failed to get irq\n",
|
||||
__func__);
|
||||
else
|
||||
enable_irq_wake(irq);
|
||||
*irq_num = irq;
|
||||
}
|
||||
|
||||
put_device(&pdev->dev);
|
||||
|
|
@ -1232,11 +1289,38 @@ static int tsens_reinit(struct tsens_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int tsens_suspend_common(struct tsens_priv *priv)
|
||||
{
|
||||
if (!device_may_wakeup(priv->dev))
|
||||
return 0;
|
||||
|
||||
if (priv->feat->combo_int)
|
||||
enable_irq_wake(priv->combined_irq);
|
||||
else {
|
||||
enable_irq_wake(priv->uplow_irq);
|
||||
if (priv->feat->crit_int)
|
||||
enable_irq_wake(priv->crit_irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsens_resume_common(struct tsens_priv *priv)
|
||||
{
|
||||
if (pm_suspend_target_state == PM_SUSPEND_MEM)
|
||||
tsens_reinit(priv);
|
||||
|
||||
if (!device_may_wakeup(priv->dev))
|
||||
return 0;
|
||||
|
||||
if (priv->feat->combo_int)
|
||||
disable_irq_wake(priv->combined_irq);
|
||||
else {
|
||||
disable_irq_wake(priv->uplow_irq);
|
||||
if (priv->feat->crit_int)
|
||||
disable_irq_wake(priv->crit_irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1276,15 +1360,18 @@ static int tsens_register(struct tsens_priv *priv)
|
|||
|
||||
if (priv->feat->combo_int) {
|
||||
ret = tsens_register_irq(priv, "combined",
|
||||
tsens_combined_irq_thread);
|
||||
tsens_combined_irq_thread, &priv->combined_irq);
|
||||
} else {
|
||||
ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
|
||||
ret = tsens_register_irq(priv, "uplow", tsens_irq_thread,
|
||||
&priv->uplow_irq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (priv->feat->crit_int)
|
||||
if (priv->feat->crit_int) {
|
||||
ret = tsens_register_irq(priv, "critical",
|
||||
tsens_critical_irq_thread);
|
||||
tsens_critical_irq_thread,
|
||||
&priv->crit_irq);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -1343,6 +1430,8 @@ static int tsens_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
device_init_wakeup(dev, !data->no_irq_wake);
|
||||
|
||||
if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
19
drivers/thermal/spacemit/Kconfig
Normal file
19
drivers/thermal/spacemit/Kconfig
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "SpacemiT thermal drivers"
|
||||
depends on ARCH_SPACEMIT || COMPILE_TEST
|
||||
|
||||
config SPACEMIT_K1_TSENSOR
|
||||
tristate "SpacemiT K1 thermal sensor driver"
|
||||
depends on THERMAL_OF
|
||||
help
|
||||
This driver provides support for the thermal sensor
|
||||
integrated in the SpacemiT K1 SoC.
|
||||
|
||||
The thermal sensor monitors temperatures for five thermal zones:
|
||||
soc, package, gpu, cluster0, and cluster1. It supports reporting
|
||||
temperature values and handling high/low threshold interrupts.
|
||||
|
||||
Say Y here if you want to enable thermal monitoring on SpacemiT K1.
|
||||
If compiled as a module, it will be called k1_tsensor.
|
||||
|
||||
endmenu
|
||||
3
drivers/thermal/spacemit/Makefile
Normal file
3
drivers/thermal/spacemit/Makefile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_SPACEMIT_K1_TSENSOR) += k1_tsensor.o
|
||||
280
drivers/thermal/spacemit/k1_tsensor.c
Normal file
280
drivers/thermal/spacemit/k1_tsensor.c
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Thermal sensor driver for SpacemiT K1 SoC
|
||||
*
|
||||
* Copyright (C) 2026 Shuwei Wu <shuwei.wu@mailbox.org>
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
#define K1_TSENSOR_PCTRL_REG 0x00
|
||||
#define K1_TSENSOR_PCTRL_ENABLE BIT(0)
|
||||
#define K1_TSENSOR_PCTRL_TEMP_MODE BIT(3)
|
||||
#define K1_TSENSOR_PCTRL_RAW_SEL BIT(7)
|
||||
|
||||
#define K1_TSENSOR_PCTRL_CTUNE GENMASK(11, 8)
|
||||
#define K1_TSENSOR_PCTRL_SW_CTRL GENMASK(21, 18)
|
||||
#define K1_TSENSOR_PCTRL_HW_AUTO_MODE BIT(23)
|
||||
|
||||
#define K1_TSENSOR_EN_REG 0x08
|
||||
#define K1_TSENSOR_EN_ALL GENMASK(MAX_SENSOR_NUMBER - 1, 0)
|
||||
|
||||
#define K1_TSENSOR_TIME_REG 0x0C
|
||||
#define K1_TSENSOR_TIME_WAIT_REF_CNT GENMASK(3, 0)
|
||||
#define K1_TSENSOR_TIME_ADC_CNT_RST GENMASK(7, 4)
|
||||
#define K1_TSENSOR_TIME_FILTER_PERIOD GENMASK(21, 20)
|
||||
#define K1_TSENSOR_TIME_MASK GENMASK(23, 0)
|
||||
|
||||
#define K1_TSENSOR_INT_CLR_REG 0x10
|
||||
#define K1_TSENSOR_INT_EN_REG 0x14
|
||||
#define K1_TSENSOR_INT_STA_REG 0x18
|
||||
|
||||
#define K1_TSENSOR_INT_EN_MASK BIT(0)
|
||||
#define K1_TSENSOR_INT_MASK(x) (GENMASK(2, 1) << ((x) * 2))
|
||||
|
||||
#define K1_TSENSOR_DATA_BASE_REG 0x20
|
||||
#define K1_TSENSOR_DATA_REG(x) (K1_TSENSOR_DATA_BASE_REG + ((x) / 2) * 4)
|
||||
#define K1_TSENSOR_DATA_LOW_MASK GENMASK(15, 0)
|
||||
#define K1_TSENSOR_DATA_HIGH_MASK GENMASK(31, 16)
|
||||
|
||||
#define K1_TSENSOR_THRSH_BASE_REG 0x40
|
||||
#define K1_TSENSOR_THRSH_REG(x) (K1_TSENSOR_THRSH_BASE_REG + ((x) * 4))
|
||||
#define K1_TSENSOR_THRSH_LOW_MASK GENMASK(15, 0)
|
||||
#define K1_TSENSOR_THRSH_HIGH_MASK GENMASK(31, 16)
|
||||
|
||||
#define MAX_SENSOR_NUMBER 5
|
||||
|
||||
/* Hardware offset value required for temperature calculation */
|
||||
#define TEMPERATURE_OFFSET 278
|
||||
|
||||
struct k1_tsensor_channel {
|
||||
struct k1_tsensor *ts;
|
||||
struct thermal_zone_device *tzd;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct k1_tsensor {
|
||||
void __iomem *base;
|
||||
struct k1_tsensor_channel ch[MAX_SENSOR_NUMBER];
|
||||
};
|
||||
|
||||
static void k1_tsensor_init(struct k1_tsensor *ts)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Disable all the interrupts */
|
||||
writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG);
|
||||
|
||||
/* Configure ADC sampling time and filter period */
|
||||
val = readl(ts->base + K1_TSENSOR_TIME_REG);
|
||||
val &= ~K1_TSENSOR_TIME_MASK;
|
||||
val |= K1_TSENSOR_TIME_FILTER_PERIOD |
|
||||
K1_TSENSOR_TIME_ADC_CNT_RST |
|
||||
K1_TSENSOR_TIME_WAIT_REF_CNT;
|
||||
writel(val, ts->base + K1_TSENSOR_TIME_REG);
|
||||
|
||||
/*
|
||||
* Enable all sensors' auto mode, enable dither control,
|
||||
* consecutive mode, and power up sensor.
|
||||
*/
|
||||
val = readl(ts->base + K1_TSENSOR_PCTRL_REG);
|
||||
val &= ~K1_TSENSOR_PCTRL_SW_CTRL;
|
||||
val &= ~K1_TSENSOR_PCTRL_CTUNE;
|
||||
val |= K1_TSENSOR_PCTRL_RAW_SEL |
|
||||
K1_TSENSOR_PCTRL_TEMP_MODE |
|
||||
K1_TSENSOR_PCTRL_HW_AUTO_MODE |
|
||||
K1_TSENSOR_PCTRL_ENABLE;
|
||||
writel(val, ts->base + K1_TSENSOR_PCTRL_REG);
|
||||
|
||||
/* Enable each sensor */
|
||||
val = readl(ts->base + K1_TSENSOR_EN_REG);
|
||||
val |= K1_TSENSOR_EN_ALL;
|
||||
writel(val, ts->base + K1_TSENSOR_EN_REG);
|
||||
}
|
||||
|
||||
static void k1_tsensor_enable_irq(struct k1_tsensor_channel *ch)
|
||||
{
|
||||
struct k1_tsensor *ts = ch->ts;
|
||||
u32 val;
|
||||
|
||||
val = readl(ts->base + K1_TSENSOR_INT_CLR_REG);
|
||||
val |= K1_TSENSOR_INT_MASK(ch->id);
|
||||
writel(val, ts->base + K1_TSENSOR_INT_CLR_REG);
|
||||
|
||||
val = readl(ts->base + K1_TSENSOR_INT_EN_REG);
|
||||
val &= ~K1_TSENSOR_INT_MASK(ch->id);
|
||||
writel(val, ts->base + K1_TSENSOR_INT_EN_REG);
|
||||
|
||||
/* Enable thermal interrupt */
|
||||
val = readl(ts->base + K1_TSENSOR_INT_EN_REG);
|
||||
val |= K1_TSENSOR_INT_EN_MASK;
|
||||
writel(val, ts->base + K1_TSENSOR_INT_EN_REG);
|
||||
}
|
||||
|
||||
/*
|
||||
* The conversion formula used is:
|
||||
* T(m°C) = (((raw_value & mask) >> shift) - TEMPERATURE_OFFSET) * 1000
|
||||
*/
|
||||
static int k1_tsensor_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz);
|
||||
struct k1_tsensor *ts = ch->ts;
|
||||
u32 val;
|
||||
|
||||
val = readl(ts->base + K1_TSENSOR_DATA_REG(ch->id));
|
||||
if (ch->id % 2)
|
||||
*temp = FIELD_GET(K1_TSENSOR_DATA_HIGH_MASK, val);
|
||||
else
|
||||
*temp = FIELD_GET(K1_TSENSOR_DATA_LOW_MASK, val);
|
||||
|
||||
*temp -= TEMPERATURE_OFFSET;
|
||||
*temp *= 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each sensor, the hardware threshold register is 32 bits:
|
||||
* - Lower 16 bits [15:0] configure the low threshold temperature.
|
||||
* - Upper 16 bits [31:16] configure the high threshold temperature.
|
||||
*/
|
||||
static int k1_tsensor_set_trips(struct thermal_zone_device *tz, int low, int high)
|
||||
{
|
||||
struct k1_tsensor_channel *ch = thermal_zone_device_priv(tz);
|
||||
struct k1_tsensor *ts = ch->ts;
|
||||
u32 val;
|
||||
|
||||
if (low >= high)
|
||||
return -EINVAL;
|
||||
|
||||
low = clamp_val(low / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET,
|
||||
FIELD_MAX(K1_TSENSOR_THRSH_LOW_MASK));
|
||||
high = clamp_val(high / 1000 + TEMPERATURE_OFFSET, TEMPERATURE_OFFSET,
|
||||
FIELD_MAX(K1_TSENSOR_THRSH_HIGH_MASK));
|
||||
|
||||
val = readl(ts->base + K1_TSENSOR_THRSH_REG(ch->id));
|
||||
|
||||
val &= ~(K1_TSENSOR_THRSH_LOW_MASK | K1_TSENSOR_THRSH_HIGH_MASK);
|
||||
val |= FIELD_PREP(K1_TSENSOR_THRSH_LOW_MASK, low);
|
||||
val |= FIELD_PREP(K1_TSENSOR_THRSH_HIGH_MASK, high);
|
||||
|
||||
writel(val, ts->base + K1_TSENSOR_THRSH_REG(ch->id));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops k1_tsensor_ops = {
|
||||
.get_temp = k1_tsensor_get_temp,
|
||||
.set_trips = k1_tsensor_set_trips,
|
||||
};
|
||||
|
||||
static irqreturn_t k1_tsensor_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct k1_tsensor *ts = (struct k1_tsensor *)data;
|
||||
int mask, status, i;
|
||||
|
||||
status = readl(ts->base + K1_TSENSOR_INT_STA_REG);
|
||||
|
||||
for (i = 0; i < MAX_SENSOR_NUMBER; i++) {
|
||||
if (status & K1_TSENSOR_INT_MASK(i)) {
|
||||
mask = readl(ts->base + K1_TSENSOR_INT_CLR_REG);
|
||||
mask |= K1_TSENSOR_INT_MASK(i);
|
||||
writel(mask, ts->base + K1_TSENSOR_INT_CLR_REG);
|
||||
thermal_zone_device_update(ts->ch[i].tzd, THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int k1_tsensor_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct k1_tsensor *ts;
|
||||
struct reset_control *reset;
|
||||
struct clk *clk;
|
||||
int i, irq, ret;
|
||||
|
||||
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ts->base))
|
||||
return dev_err_probe(dev, PTR_ERR(ts->base), "Failed to get reg\n");
|
||||
|
||||
reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return dev_err_probe(dev, PTR_ERR(reset), "Failed to get/deassert reset control\n");
|
||||
|
||||
clk = devm_clk_get_enabled(dev, "core");
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "Failed to get core clock\n");
|
||||
|
||||
clk = devm_clk_get_enabled(dev, "bus");
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "Failed to get bus clock\n");
|
||||
|
||||
k1_tsensor_init(ts);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
k1_tsensor_irq_thread,
|
||||
IRQF_ONESHOT, "k1_tsensor", ts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < MAX_SENSOR_NUMBER; ++i) {
|
||||
ts->ch[i].id = i;
|
||||
ts->ch[i].ts = ts;
|
||||
ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops);
|
||||
if (IS_ERR(ts->ch[i].tzd))
|
||||
return PTR_ERR(ts->ch[i].tzd);
|
||||
|
||||
/* Attach sysfs hwmon attributes for userspace monitoring */
|
||||
ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
|
||||
|
||||
k1_tsensor_enable_irq(ts->ch + i);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id k1_tsensor_dt_ids[] = {
|
||||
{ .compatible = "spacemit,k1-tsensor" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, k1_tsensor_dt_ids);
|
||||
|
||||
static struct platform_driver k1_tsensor_driver = {
|
||||
.driver = {
|
||||
.name = "k1_tsensor",
|
||||
.of_match_table = k1_tsensor_dt_ids,
|
||||
},
|
||||
.probe = k1_tsensor_probe,
|
||||
};
|
||||
module_platform_driver(k1_tsensor_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SpacemiT K1 Thermal Sensor Driver");
|
||||
MODULE_AUTHOR("Shuwei Wu <shuwei.wu@mailbox.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -116,78 +116,24 @@ static int thermal_set_governor(struct thermal_zone_device *tz,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int thermal_register_governor(struct thermal_governor *governor)
|
||||
static int __init thermal_register_governor(struct thermal_governor *governor)
|
||||
{
|
||||
int err;
|
||||
const char *name;
|
||||
struct thermal_zone_device *pos;
|
||||
|
||||
if (!governor)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&thermal_governor_lock);
|
||||
if (__find_governor(governor->name))
|
||||
return -EBUSY;
|
||||
|
||||
err = -EBUSY;
|
||||
if (!__find_governor(governor->name)) {
|
||||
bool match_default;
|
||||
list_add(&governor->governor_list, &thermal_governor_list);
|
||||
|
||||
err = 0;
|
||||
list_add(&governor->governor_list, &thermal_governor_list);
|
||||
match_default = !strncmp(governor->name,
|
||||
DEFAULT_THERMAL_GOVERNOR,
|
||||
THERMAL_NAME_LENGTH);
|
||||
if (strncmp(governor->name, DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
|
||||
return 0;
|
||||
|
||||
if (!def_governor && match_default)
|
||||
def_governor = governor;
|
||||
}
|
||||
if (!def_governor)
|
||||
def_governor = governor;
|
||||
|
||||
guard(mutex)(&thermal_list_lock);
|
||||
|
||||
list_for_each_entry(pos, &thermal_tz_list, node) {
|
||||
/*
|
||||
* only thermal zones with specified tz->tzp->governor_name
|
||||
* may run with tz->govenor unset
|
||||
*/
|
||||
if (pos->governor)
|
||||
continue;
|
||||
|
||||
name = pos->tzp->governor_name;
|
||||
|
||||
if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_set_governor(pos, governor);
|
||||
if (ret)
|
||||
dev_err(&pos->device,
|
||||
"Failed to set governor %s for thermal zone %s: %d\n",
|
||||
governor->name, pos->type, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void thermal_unregister_governor(struct thermal_governor *governor)
|
||||
{
|
||||
struct thermal_zone_device *pos;
|
||||
|
||||
if (!governor)
|
||||
return;
|
||||
|
||||
guard(mutex)(&thermal_governor_lock);
|
||||
|
||||
if (!__find_governor(governor->name))
|
||||
return;
|
||||
|
||||
list_del(&governor->governor_list);
|
||||
|
||||
guard(mutex)(&thermal_list_lock);
|
||||
|
||||
list_for_each_entry(pos, &thermal_tz_list, node) {
|
||||
if (!strncasecmp(pos->governor->name, governor->name,
|
||||
THERMAL_NAME_LENGTH))
|
||||
thermal_set_governor(pos, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
|
||||
|
|
@ -225,40 +171,34 @@ int thermal_build_list_of_policies(char *buf)
|
|||
|
||||
static void __init thermal_unregister_governors(void)
|
||||
{
|
||||
struct thermal_governor **governor;
|
||||
struct thermal_governor *gov, *pos;
|
||||
|
||||
for_each_governor_table(governor)
|
||||
thermal_unregister_governor(*governor);
|
||||
guard(mutex)(&thermal_governor_lock);
|
||||
|
||||
list_for_each_entry_safe(gov, pos, &thermal_governor_list, governor_list)
|
||||
list_del(&gov->governor_list);
|
||||
}
|
||||
|
||||
static int __init thermal_register_governors(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct thermal_governor **governor;
|
||||
|
||||
guard(mutex)(&thermal_governor_lock);
|
||||
|
||||
for_each_governor_table(governor) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_register_governor(*governor);
|
||||
if (ret) {
|
||||
pr_err("Failed to register governor: '%s'",
|
||||
(*governor)->name);
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("Registered thermal governor '%s'",
|
||||
(*governor)->name);
|
||||
pr_info("Registered thermal governor '%s'", (*governor)->name);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
struct thermal_governor **gov;
|
||||
|
||||
for_each_governor_table(gov) {
|
||||
if (gov == governor)
|
||||
break;
|
||||
thermal_unregister_governor(*gov);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz,
|
||||
|
|
@ -949,34 +889,7 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
|
|||
kfree(pos);
|
||||
}
|
||||
|
||||
static void thermal_release(struct device *dev)
|
||||
{
|
||||
struct thermal_zone_device *tz;
|
||||
struct thermal_cooling_device *cdev;
|
||||
|
||||
if (!strncmp(dev_name(dev), "thermal_zone",
|
||||
sizeof("thermal_zone") - 1)) {
|
||||
tz = to_thermal_zone(dev);
|
||||
thermal_zone_destroy_device_groups(tz);
|
||||
thermal_set_governor(tz, NULL);
|
||||
ida_destroy(&tz->ida);
|
||||
mutex_destroy(&tz->lock);
|
||||
complete(&tz->removal);
|
||||
} else if (!strncmp(dev_name(dev), "cooling_device",
|
||||
sizeof("cooling_device") - 1)) {
|
||||
cdev = to_cooling_device(dev);
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
kfree_const(cdev->type);
|
||||
ida_free(&thermal_cdev_ida, cdev->id);
|
||||
kfree(cdev);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct class thermal_class = {
|
||||
.name = "thermal",
|
||||
.dev_release = thermal_release,
|
||||
};
|
||||
static bool thermal_class_unavailable __ro_after_init = true;
|
||||
static struct class *thermal_class __ro_after_init;
|
||||
|
||||
static inline
|
||||
void print_bind_err_msg(struct thermal_zone_device *tz,
|
||||
|
|
@ -1040,42 +953,35 @@ static void thermal_cooling_device_init_complete(struct thermal_cooling_device *
|
|||
thermal_zone_cdev_bind(tz, cdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* __thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
* It also gives the opportunity to link the cooling device to a device tree
|
||||
* node, so that it can be bound to a thermal zone created out of device tree.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
static struct thermal_cooling_device *
|
||||
__thermal_cooling_device_register(struct device_node *np,
|
||||
const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
static void thermal_cdev_release(struct device *dev)
|
||||
{
|
||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
kfree_const(cdev->type);
|
||||
ida_free(&thermal_cdev_ida, cdev->id);
|
||||
kfree(cdev);
|
||||
}
|
||||
|
||||
struct thermal_cooling_device *
|
||||
thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
unsigned long current_state;
|
||||
int ret;
|
||||
|
||||
if (!ops || !ops->get_max_state || !ops->get_cur_state ||
|
||||
!ops->set_cur_state)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (thermal_class_unavailable)
|
||||
if (!thermal_class)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
cdev = kzalloc_obj(*cdev);
|
||||
if (!cdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cdev->ops = ops;
|
||||
|
||||
ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto out_kfree_cdev;
|
||||
|
|
@ -1087,17 +993,35 @@ __thermal_cooling_device_register(struct device_node *np,
|
|||
goto out_ida_remove;
|
||||
}
|
||||
|
||||
return cdev;
|
||||
|
||||
out_ida_remove:
|
||||
ida_free(&thermal_cdev_ida, cdev->id);
|
||||
out_kfree_cdev:
|
||||
kfree(cdev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata)
|
||||
{
|
||||
unsigned long current_state;
|
||||
int ret;
|
||||
|
||||
mutex_init(&cdev->lock);
|
||||
INIT_LIST_HEAD(&cdev->thermal_instances);
|
||||
cdev->np = np;
|
||||
cdev->ops = ops;
|
||||
cdev->updated = false;
|
||||
cdev->device.class = &thermal_class;
|
||||
cdev->device.class = thermal_class;
|
||||
cdev->device.release = thermal_cdev_release;
|
||||
device_initialize(&cdev->device);
|
||||
cdev->devdata = devdata;
|
||||
|
||||
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
||||
if (ret)
|
||||
goto out_put_device;
|
||||
|
||||
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
|
||||
if (ret)
|
||||
goto out_cdev_type;
|
||||
goto out_put_device;
|
||||
|
||||
/*
|
||||
* The cooling device's current state is only needed for debug
|
||||
|
|
@ -1113,40 +1037,32 @@ __thermal_cooling_device_register(struct device_node *np,
|
|||
|
||||
thermal_cooling_device_setup_sysfs(cdev);
|
||||
|
||||
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
||||
ret = device_add(&cdev->device);
|
||||
if (ret)
|
||||
goto out_cooling_dev;
|
||||
|
||||
ret = device_register(&cdev->device);
|
||||
if (ret) {
|
||||
/* thermal_release() handles rest of the cleanup */
|
||||
put_device(&cdev->device);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
goto out_put_device;
|
||||
|
||||
if (current_state <= cdev->max_state)
|
||||
thermal_debug_cdev_add(cdev, current_state);
|
||||
|
||||
thermal_cooling_device_init_complete(cdev);
|
||||
|
||||
return cdev;
|
||||
return 0;
|
||||
|
||||
out_cooling_dev:
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
out_cdev_type:
|
||||
kfree_const(cdev->type);
|
||||
out_ida_remove:
|
||||
ida_free(&thermal_cdev_ida, cdev->id);
|
||||
out_kfree_cdev:
|
||||
kfree(cdev);
|
||||
return ERR_PTR(ret);
|
||||
out_put_device:
|
||||
/*
|
||||
* The device core will release the memory via
|
||||
* thermal_release() after put_device() is called in the error
|
||||
* path
|
||||
*/
|
||||
put_device(&cdev->device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
|
|
@ -1159,82 +1075,62 @@ struct thermal_cooling_device *
|
|||
thermal_cooling_device_register(const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
return __thermal_cooling_device_register(NULL, type, devdata, ops);
|
||||
struct thermal_cooling_device *cdev;
|
||||
int ret;
|
||||
|
||||
cdev = thermal_cooling_device_alloc(type, ops);
|
||||
if (IS_ERR(cdev))
|
||||
return cdev;
|
||||
|
||||
ret = thermal_cooling_device_add(cdev, devdata);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return cdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_register);
|
||||
|
||||
/**
|
||||
* thermal_of_cooling_device_register() - register an OF thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This function will register a cooling device with device tree node reference.
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_of_cooling_device_register(struct device_node *np,
|
||||
const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
static void thermal_cooling_device_release(void *data)
|
||||
{
|
||||
return __thermal_cooling_device_register(np, type, devdata, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
|
||||
struct thermal_cooling_device *cdev = data;
|
||||
|
||||
static void thermal_cooling_device_release(struct device *dev, void *res)
|
||||
{
|
||||
thermal_cooling_device_unregister(
|
||||
*(struct thermal_cooling_device **)res);
|
||||
thermal_cooling_device_unregister(cdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_thermal_of_cooling_device_register() - register an OF thermal cooling
|
||||
* device
|
||||
* devm_thermal_cooling_device_register() - register a thermal cooling device
|
||||
*
|
||||
* @dev: a valid struct device pointer of a sensor device.
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This function will register a cooling device with device tree node reference.
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
* This function will register a cooling device. This interface
|
||||
* function adds a new thermal cooling device (fan/processor/...) to
|
||||
* /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind
|
||||
* itself to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
devm_thermal_of_cooling_device_register(struct device *dev,
|
||||
struct device_node *np,
|
||||
const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
devm_thermal_cooling_device_register(struct device *dev, const char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
struct thermal_cooling_device **ptr, *tcd;
|
||||
struct thermal_cooling_device *cdev;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
cdev = thermal_cooling_device_register(type, devdata, ops);
|
||||
if (IS_ERR(cdev))
|
||||
return cdev;
|
||||
|
||||
tcd = __thermal_cooling_device_register(np, type, devdata, ops);
|
||||
if (IS_ERR(tcd)) {
|
||||
devres_free(ptr);
|
||||
return tcd;
|
||||
}
|
||||
ret = devm_add_action_or_reset(dev, thermal_cooling_device_release, cdev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
*ptr = tcd;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return tcd;
|
||||
return cdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register);
|
||||
EXPORT_SYMBOL_GPL(devm_thermal_cooling_device_register);
|
||||
|
||||
static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
|
|
@ -1470,6 +1366,17 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz)
|
|||
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static void thermal_zone_device_release(struct device *dev)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
|
||||
thermal_zone_destroy_device_groups(tz);
|
||||
thermal_set_governor(tz, NULL);
|
||||
ida_destroy(&tz->ida);
|
||||
mutex_destroy(&tz->lock);
|
||||
complete(&tz->removal);
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_zone_device_register_with_trips() - register a new thermal zone device
|
||||
* @type: the thermal zone device type
|
||||
|
|
@ -1540,7 +1447,7 @@ thermal_zone_device_register_with_trips(const char *type,
|
|||
if (polling_delay && passive_delay > polling_delay)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (thermal_class_unavailable)
|
||||
if (!thermal_class)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
tz = kzalloc_flex(*tz, trips, num_trips);
|
||||
|
|
@ -1576,7 +1483,8 @@ thermal_zone_device_register_with_trips(const char *type,
|
|||
if (!tz->ops.critical)
|
||||
tz->ops.critical = thermal_zone_device_critical;
|
||||
|
||||
tz->device.class = &thermal_class;
|
||||
tz->device.class = thermal_class;
|
||||
tz->device.release = thermal_zone_device_release;
|
||||
tz->devdata = devdata;
|
||||
tz->num_trips = num_trips;
|
||||
for_each_trip_desc(tz, td) {
|
||||
|
|
@ -1837,7 +1745,7 @@ static void __thermal_pm_prepare(void)
|
|||
|
||||
void thermal_pm_prepare(void)
|
||||
{
|
||||
if (thermal_class_unavailable)
|
||||
if (!thermal_class)
|
||||
return;
|
||||
|
||||
__thermal_pm_prepare();
|
||||
|
|
@ -1868,7 +1776,7 @@ void thermal_pm_complete(void)
|
|||
{
|
||||
struct thermal_zone_device *tz;
|
||||
|
||||
if (thermal_class_unavailable)
|
||||
if (!thermal_class)
|
||||
return;
|
||||
|
||||
guard(mutex)(&thermal_list_lock);
|
||||
|
|
@ -1881,6 +1789,7 @@ void thermal_pm_complete(void)
|
|||
|
||||
static int __init thermal_init(void)
|
||||
{
|
||||
struct class *tc;
|
||||
int result;
|
||||
|
||||
thermal_debug_init();
|
||||
|
|
@ -1889,27 +1798,27 @@ static int __init thermal_init(void)
|
|||
if (result)
|
||||
goto error;
|
||||
|
||||
thermal_wq = alloc_workqueue("thermal_events", WQ_POWER_EFFICIENT, 0);
|
||||
thermal_wq = alloc_workqueue("thermal_events", WQ_UNBOUND, 0);
|
||||
if (!thermal_wq) {
|
||||
result = -ENOMEM;
|
||||
goto unregister_netlink;
|
||||
}
|
||||
|
||||
result = thermal_register_governors();
|
||||
if (result)
|
||||
goto destroy_workqueue;
|
||||
|
||||
result = class_register(&thermal_class);
|
||||
if (result)
|
||||
goto unregister_governors;
|
||||
|
||||
thermal_class_unavailable = false;
|
||||
tc = class_create("thermal");
|
||||
if (IS_ERR(tc)) {
|
||||
result = PTR_ERR(tc);
|
||||
goto unregister_governors;
|
||||
}
|
||||
|
||||
thermal_class = tc;
|
||||
return 0;
|
||||
|
||||
unregister_governors:
|
||||
thermal_unregister_governors();
|
||||
destroy_workqueue:
|
||||
destroy_workqueue(thermal_wq);
|
||||
unregister_netlink:
|
||||
thermal_netlink_exit();
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -19,27 +19,19 @@
|
|||
#include "thermal_hwmon.h"
|
||||
#include "thermal_core.h"
|
||||
|
||||
/*
|
||||
* Needs to be large enough to hold a thermal zone type string followed by an
|
||||
* underline character and a 32-bit integer in decimal representation.
|
||||
*/
|
||||
#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11)
|
||||
|
||||
/* hwmon sys I/F */
|
||||
/* thermal zone devices with the same type share one hwmon device */
|
||||
struct thermal_hwmon_device {
|
||||
char type[THERMAL_NAME_LENGTH];
|
||||
char name[THERMAL_HWMON_NAME_LENGTH];
|
||||
struct device *device;
|
||||
int count;
|
||||
struct list_head tz_list;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct thermal_hwmon_attr {
|
||||
struct device_attribute attr;
|
||||
char name[16];
|
||||
};
|
||||
|
||||
/* one temperature input for each thermal zone */
|
||||
struct thermal_hwmon_temp {
|
||||
struct list_head hwmon_node;
|
||||
struct thermal_zone_device *tz;
|
||||
struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
|
||||
struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
|
||||
};
|
||||
|
||||
static LIST_HEAD(thermal_hwmon_list);
|
||||
|
|
@ -47,19 +39,14 @@ static LIST_HEAD(thermal_hwmon_list);
|
|||
static DEFINE_MUTEX(thermal_hwmon_list_lock);
|
||||
|
||||
static ssize_t
|
||||
temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
|
||||
struct thermal_zone_device *tz = hwmon->tz;
|
||||
int temperature;
|
||||
int ret;
|
||||
struct thermal_hwmon_attr *hwmon_attr
|
||||
= container_of(attr, struct thermal_hwmon_attr, attr);
|
||||
struct thermal_hwmon_temp *temp
|
||||
= container_of(hwmon_attr, struct thermal_hwmon_temp,
|
||||
temp_input);
|
||||
struct thermal_zone_device *tz = temp->tz;
|
||||
|
||||
ret = thermal_zone_get_temp(tz, &temperature);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -67,14 +54,10 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct thermal_hwmon_attr *hwmon_attr
|
||||
= container_of(attr, struct thermal_hwmon_attr, attr);
|
||||
struct thermal_hwmon_temp *temp
|
||||
= container_of(hwmon_attr, struct thermal_hwmon_temp,
|
||||
temp_crit);
|
||||
struct thermal_zone_device *tz = temp->tz;
|
||||
struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
|
||||
struct thermal_zone_device *tz = hwmon->tz;
|
||||
int temperature;
|
||||
int ret;
|
||||
|
||||
|
|
@ -87,167 +70,98 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
return sysfs_emit(buf, "%d\n", temperature);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(temp1_input);
|
||||
static DEVICE_ATTR_RO(temp1_crit);
|
||||
|
||||
static struct thermal_hwmon_device *
|
||||
thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
|
||||
static struct attribute *thermal_hwmon_attrs[] = {
|
||||
&dev_attr_temp1_input.attr,
|
||||
&dev_attr_temp1_crit.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t thermal_hwmon_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
char type[THERMAL_NAME_LENGTH];
|
||||
if (a == &dev_attr_temp1_input.attr)
|
||||
return a->mode;
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
|
||||
strscpy(type, tz->type);
|
||||
strreplace(type, '-', '_');
|
||||
if (!strcmp(hwmon->type, type)) {
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
return hwmon;
|
||||
}
|
||||
if (a == &dev_attr_temp1_crit.attr) {
|
||||
struct thermal_hwmon_device *hwmon = dev_get_drvdata(kobj_to_dev(kobj));
|
||||
struct thermal_zone_device *tz = hwmon->tz;
|
||||
int dummy;
|
||||
|
||||
if (tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &dummy))
|
||||
return a->mode;
|
||||
}
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the temperature input matching a given thermal zone */
|
||||
static struct thermal_hwmon_temp *
|
||||
thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
|
||||
const struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_hwmon_temp *temp;
|
||||
static const struct attribute_group thermal_hwmon_group = {
|
||||
.attrs = thermal_hwmon_attrs,
|
||||
.is_visible = thermal_hwmon_attr_is_visible,
|
||||
};
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
|
||||
if (temp->tz == tz) {
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
return temp;
|
||||
}
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
|
||||
{
|
||||
int temp;
|
||||
return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp);
|
||||
}
|
||||
__ATTRIBUTE_GROUPS(thermal_hwmon);
|
||||
|
||||
int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
struct thermal_hwmon_temp *temp;
|
||||
int new_hwmon_device = 1;
|
||||
int result;
|
||||
|
||||
hwmon = thermal_hwmon_lookup_by_type(tz);
|
||||
if (hwmon) {
|
||||
new_hwmon_device = 0;
|
||||
goto register_sys_interface;
|
||||
}
|
||||
|
||||
hwmon = kzalloc_obj(*hwmon);
|
||||
if (!hwmon)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&hwmon->tz_list);
|
||||
strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
|
||||
strreplace(hwmon->type, '-', '_');
|
||||
hwmon->tz = tz;
|
||||
/*
|
||||
* Append the thermal zone ID preceded by an underline character to the
|
||||
* type to disambiguate the sensors command output.
|
||||
*/
|
||||
scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id);
|
||||
strreplace(hwmon->name, '-', '_');
|
||||
hwmon->device = hwmon_device_register_for_thermal(&tz->device,
|
||||
hwmon->type, hwmon);
|
||||
hwmon->name, hwmon,
|
||||
thermal_hwmon_groups);
|
||||
if (IS_ERR(hwmon->device)) {
|
||||
result = PTR_ERR(hwmon->device);
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
register_sys_interface:
|
||||
temp = kzalloc_obj(*temp);
|
||||
if (!temp) {
|
||||
result = -ENOMEM;
|
||||
goto unregister_name;
|
||||
}
|
||||
|
||||
temp->tz = tz;
|
||||
hwmon->count++;
|
||||
|
||||
snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
|
||||
"temp%d_input", hwmon->count);
|
||||
temp->temp_input.attr.attr.name = temp->temp_input.name;
|
||||
temp->temp_input.attr.attr.mode = 0444;
|
||||
temp->temp_input.attr.show = temp_input_show;
|
||||
sysfs_attr_init(&temp->temp_input.attr.attr);
|
||||
result = device_create_file(hwmon->device, &temp->temp_input.attr);
|
||||
if (result)
|
||||
goto free_temp_mem;
|
||||
|
||||
if (thermal_zone_crit_temp_valid(tz)) {
|
||||
snprintf(temp->temp_crit.name,
|
||||
sizeof(temp->temp_crit.name),
|
||||
"temp%d_crit", hwmon->count);
|
||||
temp->temp_crit.attr.attr.name = temp->temp_crit.name;
|
||||
temp->temp_crit.attr.attr.mode = 0444;
|
||||
temp->temp_crit.attr.show = temp_crit_show;
|
||||
sysfs_attr_init(&temp->temp_crit.attr.attr);
|
||||
result = device_create_file(hwmon->device,
|
||||
&temp->temp_crit.attr);
|
||||
if (result)
|
||||
goto unregister_input;
|
||||
int result = PTR_ERR(hwmon->device);
|
||||
|
||||
kfree(hwmon);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The list is needed for hwmon lookup during removal. */
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
if (new_hwmon_device)
|
||||
list_add_tail(&hwmon->node, &thermal_hwmon_list);
|
||||
list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
|
||||
list_add_tail(&hwmon->node, &thermal_hwmon_list);
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_input:
|
||||
device_remove_file(hwmon->device, &temp->temp_input.attr);
|
||||
free_temp_mem:
|
||||
kfree(temp);
|
||||
unregister_name:
|
||||
if (new_hwmon_device)
|
||||
hwmon_device_unregister(hwmon->device);
|
||||
free_mem:
|
||||
kfree(hwmon);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
|
||||
|
||||
static struct thermal_hwmon_device *
|
||||
thermal_hwmon_lookup(const struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
|
||||
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
|
||||
if (hwmon->tz == tz)
|
||||
return hwmon;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_hwmon_device *hwmon;
|
||||
struct thermal_hwmon_temp *temp;
|
||||
|
||||
hwmon = thermal_hwmon_lookup_by_type(tz);
|
||||
if (unlikely(!hwmon)) {
|
||||
/* Should never happen... */
|
||||
dev_dbg(&tz->device, "hwmon device lookup failed!\n");
|
||||
return;
|
||||
scoped_guard(mutex, &thermal_hwmon_list_lock) {
|
||||
hwmon = thermal_hwmon_lookup(tz);
|
||||
if (!hwmon)
|
||||
return;
|
||||
|
||||
list_del(&hwmon->node);
|
||||
}
|
||||
|
||||
temp = thermal_hwmon_lookup_temp(hwmon, tz);
|
||||
if (unlikely(!temp)) {
|
||||
/* Should never happen... */
|
||||
dev_dbg(&tz->device, "temperature input lookup failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
device_remove_file(hwmon->device, &temp->temp_input.attr);
|
||||
if (thermal_zone_crit_temp_valid(tz))
|
||||
device_remove_file(hwmon->device, &temp->temp_crit.attr);
|
||||
|
||||
mutex_lock(&thermal_hwmon_list_lock);
|
||||
list_del(&temp->hwmon_node);
|
||||
kfree(temp);
|
||||
if (!list_empty(&hwmon->tz_list)) {
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
return;
|
||||
}
|
||||
list_del(&hwmon->node);
|
||||
mutex_unlock(&thermal_hwmon_list_lock);
|
||||
|
||||
hwmon_device_unregister(hwmon->device);
|
||||
kfree(hwmon);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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, \
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{ }
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user