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

Pull thermal control updates from Rafael Wysocki:
 "These are mostly thermal driver updates, including new thermal drivers
  for Renesas RZ/G3S and Renesas RZ/G3E SoCs, a new power slider
  platform feature support in the Intel int340x thermal driver, a new
  Tegra114- specific SOCTHERM driver and more.

  There is also a Step-wise thermal governor update allowing it to start
  reducing cooling somewhat earlier if the temperature of the given
  thermal zone is falling down and a thermal testing code cleanup.

  Specifics:

   - Add new thermal driver for the Renesas RZ/G3S SoC (Claudiu Beznea)

   - Add new thermal driver for the Renesas RZ/G3E SoC (John Madieu)

   - Add support for new platform power slider feature to the Intel
     int340x driver (Srinivas Pandruvada).

   - Add new Tegra114-specific SOCTHERM driver and document Tegra114
     SOCTHERM Thermal Management System in DT bindings (Svyatoslav
     Ryhel)

   - Add temperature sensor channel to thermal-generic-adc (Svyatoslav
     Ryhel)

   - Add support for per-SoC default trim values to the Renesas
     rcar_gen3 thermal driver, use it for adding R-Car V4H default trim
     values, fix a comment typo in that driver and document Gen4 support
     in its Kconfig entry (Marek Vasut)

   - Fix mapping SoCs to generic Gen4 entry in the Renesas rcar_gen3
     thermal driver (Wolfram Sang)

   - Document the TSU unit in the r9a08g045-tsu and r9a09g047-tsu DT
     bindings (Claudiu Beznea, John Madieu)

   - Make LMH select QCOM_SCM and add missing IRQ includes to the
     qcom/lmh thermal driver (Dmitry Baryshkov)

   - Fix incorrect error message in the qcom/lmh thermal driver (Sumeet
     Pawnikar)

   - Add QCS615 compatible to tsens thermal DT bindings (Gaurav Kohli)

   - Document the Glymur temperature sensor in qcom-tsens thermal DT
     bindings (Manaf Meethalavalappu Pallikunhi)

   - Make k3_j72xx_bandgap thermal driver register the thermal sensor
     with hwmon (Michael Walle)

   - Tighten GRF requirements in the rockchip thermal DT bindings,
     silence a GRF warning in the rockchip thermal driver and unify
     struct rockchip_tsadc_chip format in it (Sebastian Reichel)

   - Update the Step-wise thermal governor to allow it to reduce the
     cooling level earlier if thermal zone temperature is dropping and
     clean it up (Rafael Wysocki)

   - Clean up the thermal testing code (Rafael Wysocki)

   - Assorted cleanups of thermal drivers (Jiapeng Chong, Salah Triki,
     Osama Abdelkader)"

* tag 'thermal-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (37 commits)
  thermal/drivers/renesas/rzg3e: Fix add thermal driver for the Renesas RZ/G3E SoC
  dt-bindings: thermal: qcom-tsens: Document the Glymur temperature Sensor
  thermal/drivers/renesas/rzg3e: Add thermal driver for the Renesas RZ/G3E SoC
  dt-bindings: thermal: r9a09g047-tsu: Document the TSU unit
  thermal/drivers/thermal-generic-adc: Add temperature sensor channel
  dt-bindings: thermal: rockchip: Tighten grf requirements
  thermal/drivers/rockchip: Shut up GRF warning
  thermal/drivers/rockchip: Unify struct rockchip_tsadc_chip format
  thermal/drivers/renesas/rzg3s: Add thermal driver for the Renesas RZ/G3S SoC
  dt-bindings: thermal: r9a08g045-tsu: Document the TSU unit
  thermal/drivers/k3_j72xx_bandgap: Register sensors with hwmon
  thermal/drivers/rcar_gen3: Fix mapping SoCs to generic Gen4 entry
  thermal/drivers/tegra: Add Tegra114 specific SOCTHERM driver
  dt-bindings: thermal: add Tegra114 soctherm header
  thermal/drivers/tegra/soctherm-fuse: Prepare calibration for Tegra114 support
  dt-bindings: thermal: Document Tegra114 SOCTHERM Thermal Management System
  thermal/drivers/rcar_gen3: Document Gen4 support in Kconfig entry
  thermal/drivers/rcar_gen3: Fix comment typo
  drivers/thermal/qcom/lmh: Fix incorrect error message
  thermal/drivers/qcom/lmh: Add missing IRQ includes
  ...
This commit is contained in:
Linus Torvalds
2025-10-01 16:33:14 -07:00
37 changed files with 1804 additions and 94 deletions

View File

@@ -18,6 +18,7 @@ description: The SOCTHERM IP block contains thermal sensors, support for
properties:
compatible:
enum:
- nvidia,tegra114-soctherm
- nvidia,tegra124-soctherm
- nvidia,tegra132-soctherm
- nvidia,tegra210-soctherm
@@ -206,6 +207,7 @@ allOf:
compatible:
contains:
enum:
- nvidia,tegra114-soctherm
- nvidia,tegra124-soctherm
- nvidia,tegra210-soctherm
- nvidia,tegra210b01-soctherm

View File

@@ -49,11 +49,13 @@ properties:
- description: v2 of TSENS
items:
- enum:
- qcom,glymur-tsens
- qcom,milos-tsens
- qcom,msm8953-tsens
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,qcm2290-tsens
- qcom,qcs615-tsens
- qcom,sa8255p-tsens
- qcom,sa8775p-tsens
- qcom,sar2130p-tsens

View File

@@ -0,0 +1,93 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/renesas,r9a08g045-tsu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3S Thermal Sensor Unit
description:
The thermal sensor unit (TSU) measures the temperature(Tj) inside
the LSI.
maintainers:
- Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
$ref: thermal-sensor.yaml#
properties:
compatible:
const: renesas,r9a08g045-tsu
reg:
maxItems: 1
clocks:
items:
- description: TSU module clock
power-domains:
maxItems: 1
resets:
items:
- description: TSU module reset
io-channels:
items:
- description: ADC channel which reports the TSU temperature
io-channel-names:
items:
- const: tsu
"#thermal-sensor-cells":
const: 0
required:
- compatible
- reg
- clocks
- power-domains
- resets
- io-channels
- io-channel-names
- '#thermal-sensor-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/r9a08g045-cpg.h>
tsu: thermal@10059000 {
compatible = "renesas,r9a08g045-tsu";
reg = <0x10059000 0x1000>;
clocks = <&cpg CPG_MOD R9A08G045_TSU_PCLK>;
resets = <&cpg R9A08G045_TSU_PRESETN>;
power-domains = <&cpg>;
#thermal-sensor-cells = <0>;
io-channels = <&adc 8>;
io-channel-names = "tsu";
};
thermal-zones {
cpu-thermal {
polling-delay-passive = <250>;
polling-delay = <1000>;
thermal-sensors = <&tsu>;
trips {
sensor_crit: sensor-crit {
temperature = <125000>;
hysteresis = <1000>;
type = "critical";
};
target: trip-point {
temperature = <100000>;
hysteresis = <1000>;
type = "passive";
};
};
};
};

View File

@@ -0,0 +1,87 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/renesas,r9a09g047-tsu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3E Temperature Sensor Unit (TSU)
maintainers:
- John Madieu <john.madieu.xa@bp.renesas.com>
description:
The Temperature Sensor Unit (TSU) is an integrated thermal sensor that
monitors the chip temperature on the Renesas RZ/G3E SoC. The TSU provides
real-time temperature measurements for thermal management.
properties:
compatible:
const: renesas,r9a09g047-tsu
reg:
maxItems: 1
clocks:
maxItems: 1
resets:
maxItems: 1
power-domains:
maxItems: 1
interrupts:
items:
- description: Conversion complete interrupt signal (pulse)
- description: Comparison result interrupt signal (level)
interrupt-names:
items:
- const: adi
- const: adcmpi
"#thermal-sensor-cells":
const: 0
renesas,tsu-trim:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: phandle to system controller
- description: offset of trim registers
description:
Phandle and offset to the system controller containing the TSU
calibration trim values. The offset points to the first trim register
(OTPTSU1TRMVAL0), with the second trim register (OTPTSU1TRMVAL1) located
at offset + 4.
required:
- compatible
- reg
- clocks
- resets
- power-domains
- interrupts
- interrupt-names
- "#thermal-sensor-cells"
- renesas,tsu-trim
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/renesas,r9a09g047-cpg.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
thermal-sensor@14002000 {
compatible = "renesas,r9a09g047-tsu";
reg = <0x14002000 0x1000>;
clocks = <&cpg CPG_MOD 0x10a>;
resets = <&cpg 0xf8>;
power-domains = <&cpg>;
interrupts = <GIC_SPI 250 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 251 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "adi", "adcmpi";
#thermal-sensor-cells = <0>;
renesas,tsu-trim = <&sys 0x330>;
};

View File

@@ -119,6 +119,21 @@ required:
- resets
allOf:
- if:
properties:
compatible:
contains:
enum:
- rockchip,px30-tsadc
- rockchip,rk3366-tsadc
- rockchip,rk3399-tsadc
- rockchip,rk3568-tsadc
then:
required:
- rockchip,grf
else:
properties:
rockchip,grf: false
- if:
not:
properties:

View File

@@ -21701,6 +21701,20 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/potentiometer/renesas,x9250.yaml
F: drivers/iio/potentiometer/x9250.c
RENESAS RZ/G3S THERMAL SENSOR UNIT DRIVER
M: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml
F: drivers/thermal/renesas/rzg3s_thermal.c
RENESAS RZ/G3E THERMAL SENSOR UNIT DRIVER
M: John Madieu <john.madieu.xa@bp.renesas.com>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/thermal/renesas,r9a09g047-tsu.yaml
F: drivers/thermal/renesas/rzg3e_thermal.c
RESET CONTROLLER FRAMEWORK
M: Philipp Zabel <p.zabel@pengutronix.de>
S: Maintained

View File

@@ -20,11 +20,13 @@
* If the temperature is higher than a trip point,
* a. if the trend is THERMAL_TREND_RAISING, use higher cooling
* state for this trip point
* b. if the trend is THERMAL_TREND_DROPPING, do nothing
* b. if the trend is THERMAL_TREND_DROPPING, use a lower cooling state
* for this trip point, but keep the cooling state above the applicable
* minimum
* If the temperature is lower than a trip point,
* a. if the trend is THERMAL_TREND_RAISING, do nothing
* b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
* state for this trip point, if the cooling state already
* b. if the trend is THERMAL_TREND_DROPPING, use the minimum applicable
* cooling state for this trip point, or if the cooling state already
* equals lower limit, deactivate the thermal instance
*/
static unsigned long get_target_state(struct thermal_instance *instance,
@@ -51,6 +53,17 @@ static unsigned long get_target_state(struct thermal_instance *instance,
if (throttle) {
if (trend == THERMAL_TREND_RAISING)
return clamp(cur_state + 1, instance->lower, instance->upper);
/*
* If the zone temperature is falling, the cooling level can
* be reduced, but it should still be above the lower state of
* the given thermal instance to pull the temperature further
* down.
*/
if (trend == THERMAL_TREND_DROPPING)
return clamp(cur_state - 1,
min(instance->lower + 1, instance->upper),
instance->upper);
} else if (trend == THERMAL_TREND_DROPPING) {
if (cur_state <= instance->lower)
return THERMAL_NO_TARGET;
@@ -69,16 +82,14 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz,
const struct thermal_trip_desc *td,
int trip_threshold)
{
bool throttle = tz->temperature >= trip_threshold;
const struct thermal_trip *trip = &td->trip;
enum thermal_trend trend = get_tz_trend(tz, trip);
int trip_id = thermal_zone_trip_id(tz, trip);
struct thermal_instance *instance;
bool throttle = false;
if (tz->temperature >= trip_threshold) {
throttle = true;
if (throttle)
trace_thermal_zone_trip(tz, trip_id, trip->type);
}
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
trip_id, trip->type, trip_threshold, trend, throttle);

View File

@@ -12,6 +12,7 @@ config INT340X_THERMAL
select ACPI_THERMAL_LIB
select INTEL_SOC_DTS_IOSF_CORE
select INTEL_TCC
select ACPI_PLATFORM_PROFILE
select PROC_THERMAL_MMIO_RAPL if POWERCAP
help
Newer laptops and tablets that use ACPI may have thermal sensors and

View File

@@ -14,5 +14,6 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_power_floor.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_soc_slider.o
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

View File

@@ -220,9 +220,6 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps
int i, result = 0;
struct psvt *psvts;
if (!acpi_has_method(handle, "PSVT"))
return -ENODEV;
status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;

View File

@@ -338,10 +338,17 @@ static int tcc_offset_save = -1;
int proc_thermal_suspend(struct device *dev)
{
struct proc_thermal_device *proc_dev;
tcc_offset_save = intel_tcc_get_offset(-1);
if (tcc_offset_save < 0)
dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save);
proc_dev = dev_get_drvdata(dev);
if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER)
proc_thermal_soc_power_slider_suspend(proc_dev);
return 0;
}
EXPORT_SYMBOL_GPL(proc_thermal_suspend);
@@ -357,6 +364,9 @@ int proc_thermal_resume(struct device *dev)
if (tcc_offset_save >= 0)
intel_tcc_set_offset(-1, tcc_offset_save);
if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER)
proc_thermal_soc_power_slider_resume(proc_dev);
return 0;
}
EXPORT_SYMBOL_GPL(proc_thermal_resume);
@@ -432,8 +442,18 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
}
}
if (feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) {
ret = proc_thermal_soc_power_slider_add(pdev, proc_priv);
if (ret) {
dev_info(&pdev->dev, "failed to add soc power efficiency slider\n");
goto err_rem_wlt;
}
}
return 0;
err_rem_wlt:
proc_thermal_wt_hint_remove(pdev);
err_rem_rfim:
proc_thermal_rfim_remove(pdev);
err_rem_ptc:

View File

@@ -69,6 +69,7 @@ struct rapl_mmio_regs {
#define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40
#define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80
#define PROC_THERMAL_FEATURE_PTC 0x100
#define PROC_THERMAL_FEATURE_SOC_POWER_SLIDER 0x200
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
@@ -127,4 +128,9 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
void proc_thermal_ptc_remove(struct pci_dev *pdev);
int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv);
void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv);
#endif

View File

@@ -498,7 +498,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) },
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
{ PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |
PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR |
PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT |

View File

@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Processor Thermal Device Interface for Reading and Writing
* SoC Power Slider Values from User Space.
*
* Operation:
* The SOC_EFFICIENCY_SLIDER_0_0_0_MCHBAR register is accessed
* using the MMIO (Memory-Mapped I/O) interface with an MMIO offset of 0x5B38.
* Although this register is 64 bits wide, only bits 7:0 are used,
* and the other bits remain unchanged.
*
* Bit definitions
*
* Bits 2:0 (Slider value):
* The SoC optimizer slider value indicates the system wide energy performance
* hint. The slider has no specific units and ranges from 0 (highest
* performance) to 6 (highest energy efficiency). Value of 7 is reserved.
* Bits 3 : Reserved
* Bits 6:4 (Offset)
* Offset allows the SoC to automatically switch slider position in range
* [slider value (bits 2:0) + offset] to improve power efficiency based on
* internal SoC algorithms.
* Bit 7 (Enable):
* If this bit is set, the SoC Optimization sliders will be processed by the
* SoC firmware.
*
* Copyright (c) 2025, Intel Corporation.
*/
#include <linux/bitfield.h>
#include <linux/pci.h>
#include <linux/platform_profile.h>
#include "processor_thermal_device.h"
#define SOC_POWER_SLIDER_OFFSET 0x5B38
enum power_slider_preference {
SOC_POWER_SLIDER_PERFORMANCE,
SOC_POWER_SLIDER_BALANCE,
SOC_POWER_SLIDER_POWERSAVE,
};
#define SOC_SLIDER_VALUE_MINIMUM 0x00
#define SOC_SLIDER_VALUE_BALANCE 0x03
#define SOC_SLIDER_VALUE_MAXIMUM 0x06
#define SLIDER_MASK GENMASK_ULL(2, 0)
#define SLIDER_ENABLE_BIT 7
static u8 slider_values[] = {
[SOC_POWER_SLIDER_PERFORMANCE] = SOC_SLIDER_VALUE_MINIMUM,
[SOC_POWER_SLIDER_BALANCE] = SOC_SLIDER_VALUE_BALANCE,
[SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM,
};
/* Lock to protect module param updates */
static DEFINE_MUTEX(slider_param_lock);
static int slider_balanced_param = SOC_SLIDER_VALUE_BALANCE;
static int slider_def_balance_set(const char *arg, const struct kernel_param *kp)
{
u8 slider_val;
int ret;
guard(mutex)(&slider_param_lock);
ret = kstrtou8(arg, 16, &slider_val);
if (!ret) {
if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] ||
slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE])
return -EINVAL;
slider_balanced_param = slider_val;
}
return ret;
}
static int slider_def_balance_get(char *buf, const struct kernel_param *kp)
{
guard(mutex)(&slider_param_lock);
return sysfs_emit(buf, "%02x\n", slider_values[SOC_POWER_SLIDER_BALANCE]);
}
static const struct kernel_param_ops slider_def_balance_ops = {
.set = slider_def_balance_set,
.get = slider_def_balance_get,
};
module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644);
MODULE_PARM_DESC(slider_balance, "Set slider default value for balance");
static u8 slider_offset;
static int slider_def_offset_set(const char *arg, const struct kernel_param *kp)
{
u8 offset;
int ret;
guard(mutex)(&slider_param_lock);
ret = kstrtou8(arg, 16, &offset);
if (!ret) {
if (offset > SOC_SLIDER_VALUE_MAXIMUM)
return -EINVAL;
slider_offset = offset;
}
return ret;
}
static int slider_def_offset_get(char *buf, const struct kernel_param *kp)
{
guard(mutex)(&slider_param_lock);
return sysfs_emit(buf, "%02x\n", slider_offset);
}
static const struct kernel_param_ops slider_offset_ops = {
.set = slider_def_offset_set,
.get = slider_def_offset_get,
};
/*
* To enhance power efficiency dynamically, the firmware can optionally
* auto-adjust the slider value based on the current workload. This
* adjustment is controlled by the "slider_offset" module parameter.
* This offset permits the firmware to increase the slider value
* up to and including "SoC slider + slider offset,".
*/
module_param_cb(slider_offset, &slider_offset_ops, NULL, 0644);
MODULE_PARM_DESC(slider_offset, "Set slider offset");
/* Convert from platform power profile option to SoC slider value */
static int convert_profile_to_power_slider(enum platform_profile_option profile)
{
switch (profile) {
case PLATFORM_PROFILE_LOW_POWER:
return slider_values[SOC_POWER_SLIDER_POWERSAVE];
case PLATFORM_PROFILE_BALANCED:
return slider_values[SOC_POWER_SLIDER_BALANCE];
case PLATFORM_PROFILE_PERFORMANCE:
return slider_values[SOC_POWER_SLIDER_PERFORMANCE];
default:
break;
}
return -EOPNOTSUPP;
}
/* Convert to platform power profile option from SoC slider values */
static int convert_power_slider_to_profile(u8 slider)
{
if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE])
return PLATFORM_PROFILE_PERFORMANCE;
if (slider == slider_values[SOC_POWER_SLIDER_BALANCE])
return PLATFORM_PROFILE_BALANCED;
if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE])
return PLATFORM_PROFILE_LOW_POWER;
return -EOPNOTSUPP;
}
static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv)
{
return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET);
}
static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val)
{
writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET);
}
#define SLIDER_OFFSET_MASK GENMASK_ULL(6, 4)
static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider)
{
u64 val;
val = read_soc_slider(proc_priv);
val &= ~SLIDER_MASK;
val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT);
/* Set the slider offset from module params */
val &= ~SLIDER_OFFSET_MASK;
val |= FIELD_PREP(SLIDER_OFFSET_MASK, slider_offset);
write_soc_slider(proc_priv, val);
}
/* profile get/set callbacks are called with a profile lock, so no need for local locks */
static int power_slider_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct proc_thermal_device *proc_priv;
int slider;
proc_priv = dev_get_drvdata(dev);
if (!proc_priv)
return -EOPNOTSUPP;
guard(mutex)(&slider_param_lock);
slider_values[SOC_POWER_SLIDER_BALANCE] = slider_balanced_param;
slider = convert_profile_to_power_slider(profile);
if (slider < 0)
return slider;
set_soc_power_profile(proc_priv, slider);
return 0;
}
static int power_slider_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
struct proc_thermal_device *proc_priv;
int slider, ret;
u64 val;
proc_priv = dev_get_drvdata(dev);
if (!proc_priv)
return -EOPNOTSUPP;
val = read_soc_slider(proc_priv);
slider = FIELD_GET(SLIDER_MASK, val);
ret = convert_power_slider_to_profile(slider);
if (ret < 0)
return ret;
*profile = ret;
return 0;
}
static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices)
{
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
set_bit(PLATFORM_PROFILE_BALANCED, choices);
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
return 0;
}
static const struct platform_profile_ops power_slider_platform_profile_ops = {
.probe = power_slider_platform_profile_probe,
.profile_get = power_slider_platform_profile_get,
.profile_set = power_slider_platform_profile_set,
};
int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
{
struct device *ppdev;
set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]);
ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv,
&power_slider_platform_profile_ops);
return PTR_ERR_OR_ZERO(ppdev);
}
EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL");
static u64 soc_slider_save;
void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv)
{
soc_slider_save = read_soc_slider(proc_priv);
}
EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL");
void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv)
{
write_soc_slider(proc_priv, soc_slider_save);
}
EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL");
MODULE_IMPORT_NS("INT340X_THERMAL");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Processor Thermal Power Slider Interface");

View File

@@ -20,6 +20,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include "thermal_hwmon.h"
#define K3_VTM_DEVINFO_PWR0_OFFSET 0x4
#define K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK 0xf0
#define K3_VTM_TMPSENS0_CTRL_OFFSET 0x300
@@ -513,6 +515,8 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev)
ret = PTR_ERR(ti_thermal);
goto err_free_ref_table;
}
devm_thermal_add_hwmon_sysfs(bgp->dev, ti_thermal);
}
platform_set_drvdata(pdev, bgp);

View File

@@ -639,7 +639,7 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl,
lvts_sensor[i].low_thresh = INT_MIN;
lvts_sensor[i].high_thresh = INT_MIN;
};
}
lvts_ctrl->valid_sensor_mask = lvts_ctrl_data->valid_sensor_mask;

View File

@@ -34,7 +34,8 @@ config QCOM_SPMI_TEMP_ALARM
config QCOM_LMH
tristate "Qualcomm Limits Management Hardware"
depends on ARCH_QCOM && QCOM_SCM
depends on ARCH_QCOM || COMPILE_TEST
select QCOM_SCM
help
This enables initialization of Qualcomm limits management
hardware(LMh). LMh allows for hardware-enforced mitigation for cpus based on

View File

@@ -5,6 +5,8 @@
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -204,7 +206,7 @@ static int lmh_probe(struct platform_device *pdev)
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_LOW_THRESHOLD, temp_low,
LMH_NODE_DCVS, node_id, 0);
if (ret) {
dev_err(dev, "Error setting thermal ARM threshold%d\n", ret);
dev_err(dev, "Error setting thermal LOW threshold%d\n", ret);
return ret;
}

View File

@@ -10,13 +10,13 @@ config RCAR_THERMAL
thermal framework.
config RCAR_GEN3_THERMAL
tristate "Renesas R-Car Gen3 and RZ/G2 thermal driver"
tristate "Renesas R-Car Gen3/Gen4 and RZ/G2 thermal driver"
depends on ARCH_RENESAS || COMPILE_TEST
depends on HAS_IOMEM
depends on OF
help
Enable this to plug the R-Car Gen3 or RZ/G2 thermal sensor driver into
the Linux thermal framework.
Enable this to plug the R-Car Gen3/Gen4 or RZ/G2 thermal sensor
driver into the Linux thermal framework.
config RZG2L_THERMAL
tristate "Renesas RZ/G2L thermal driver"
@@ -26,3 +26,18 @@ config RZG2L_THERMAL
help
Enable this to plug the RZ/G2L thermal sensor driver into the Linux
thermal framework.
config RZG3S_THERMAL
tristate "Renesas RZ/G3S thermal driver"
depends on ARCH_R9A08G045 || COMPILE_TEST
depends on OF && IIO && RZG2L_ADC
help
Enable this to plug the RZ/G3S thermal sensor driver into the Linux
thermal framework.
config RZG3E_THERMAL
tristate "Renesas RZ/G3E thermal driver"
depends on ARCH_RENESAS || COMPILE_TEST
help
Enable this to plug the RZ/G3E thermal sensor driver into the Linux
thermal framework.

View File

@@ -3,3 +3,6 @@
obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_RZG2L_THERMAL) += rzg2l_thermal.o
obj-$(CONFIG_RZG3E_THERMAL) += rzg3e_thermal.o
obj-$(CONFIG_RZG3S_THERMAL) += rzg3s_thermal.o

View File

@@ -73,11 +73,17 @@ struct rcar_gen3_thermal_fuse_info {
u32 mask;
};
struct rcar_gen3_thermal_fuse_default {
u32 ptat[3];
u32 thcodes[TSC_MAX_NUM][3];
};
struct rcar_thermal_info {
int scale;
int adj_below;
int adj_above;
const struct rcar_gen3_thermal_fuse_info *fuses;
const struct rcar_gen3_thermal_fuse_default *fuse_defaults;
};
struct equation_set_coef {
@@ -165,7 +171,7 @@ static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
const struct equation_set_coef *coef;
int adj, decicelsius, reg, thcode;
/* Read register and convert to mili Celsius */
/* Read register and convert to millidegree Celsius */
reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;
if (reg < tsc->thcode[1]) {
@@ -289,6 +295,7 @@ static void rcar_gen3_thermal_fetch_fuses(struct rcar_gen3_thermal_priv *priv)
static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)
{
const struct rcar_gen3_thermal_fuse_default *fuse_defaults = priv->info->fuse_defaults;
unsigned int i;
u32 thscp;
@@ -297,24 +304,16 @@ static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)
if (!priv->info->fuses ||
(thscp & THSCP_COR_PARA_VLD) != THSCP_COR_PARA_VLD) {
/* Default THCODE values in case FUSEs are not set. */
static const int thcodes[TSC_MAX_NUM][3] = {
{ 3397, 2800, 2221 },
{ 3393, 2795, 2216 },
{ 3389, 2805, 2237 },
{ 3415, 2694, 2195 },
{ 3356, 2724, 2244 },
};
priv->ptat[0] = 2631;
priv->ptat[1] = 1509;
priv->ptat[2] = 435;
priv->ptat[0] = fuse_defaults->ptat[0];
priv->ptat[1] = fuse_defaults->ptat[1];
priv->ptat[2] = fuse_defaults->ptat[2];
for (i = 0; i < priv->num_tscs; i++) {
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
tsc->thcode[0] = thcodes[i][0];
tsc->thcode[1] = thcodes[i][1];
tsc->thcode[2] = thcodes[i][2];
tsc->thcode[0] = fuse_defaults->thcodes[i][0];
tsc->thcode[1] = fuse_defaults->thcodes[i][1];
tsc->thcode[2] = fuse_defaults->thcodes[i][2];
}
return false;
@@ -361,11 +360,33 @@ static const struct rcar_gen3_thermal_fuse_info rcar_gen3_thermal_fuse_info_gen4
.mask = GEN4_FUSE_MASK,
};
static const struct rcar_gen3_thermal_fuse_default rcar_gen3_thermal_fuse_default_info_gen3 = {
.ptat = { 2631, 1509, 435 },
.thcodes = {
{ 3397, 2800, 2221 },
{ 3393, 2795, 2216 },
{ 3389, 2805, 2237 },
{ 3415, 2694, 2195 },
{ 3356, 2724, 2244 },
},
};
static const struct rcar_gen3_thermal_fuse_default rcar_gen3_thermal_fuse_default_info_gen4 = {
.ptat = { 3274, 2164, 985 },
.thcodes = { /* All four THS units share the same trimming */
{ 3218, 2617, 1980 },
{ 3218, 2617, 1980 },
{ 3218, 2617, 1980 },
{ 3218, 2617, 1980 },
}
};
static const struct rcar_thermal_info rcar_m3w_thermal_info = {
.scale = 157,
.adj_below = -41,
.adj_above = 116,
.fuses = &rcar_gen3_thermal_fuse_info_gen3,
.fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3,
};
static const struct rcar_thermal_info rcar_gen3_thermal_info = {
@@ -373,6 +394,15 @@ static const struct rcar_thermal_info rcar_gen3_thermal_info = {
.adj_below = -41,
.adj_above = 126,
.fuses = &rcar_gen3_thermal_fuse_info_gen3,
.fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3,
};
static const struct rcar_thermal_info rcar_s4_thermal_info = {
.scale = 167,
.adj_below = -41,
.adj_above = 126,
.fuses = &rcar_gen3_thermal_fuse_info_gen4,
.fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3,
};
static const struct rcar_thermal_info rcar_gen4_thermal_info = {
@@ -380,6 +410,7 @@ static const struct rcar_thermal_info rcar_gen4_thermal_info = {
.adj_below = -41,
.adj_above = 126,
.fuses = &rcar_gen3_thermal_fuse_info_gen4,
.fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen4,
};
static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
@@ -421,7 +452,7 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
},
{
.compatible = "renesas,r8a779f0-thermal",
.data = &rcar_gen4_thermal_info,
.data = &rcar_s4_thermal_info,
},
{
.compatible = "renesas,r8a779g0-thermal",

View File

@@ -0,0 +1,547 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G3E TSU Temperature Sensor Unit
*
* Copyright (C) 2025 Renesas Electronics Corporation
*/
#include <linux/clk.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/thermal.h>
#include <linux/units.h>
#include "../thermal_hwmon.h"
/* TSU Register offsets and bits */
#define TSU_SSUSR 0x00
#define TSU_SSUSR_EN_TS BIT(0)
#define TSU_SSUSR_ADC_PD_TS BIT(1)
#define TSU_SSUSR_SOC_TS_EN BIT(2)
#define TSU_STRGR 0x04
#define TSU_STRGR_ADST BIT(0)
#define TSU_SOSR1 0x08
#define TSU_SOSR1_ADCT_8 0x03
#define TSU_SOSR1_ADCS BIT(4)
#define TSU_SOSR1_OUTSEL BIT(9)
#define TSU_SCRR 0x10
#define TSU_SCRR_OUT12BIT_TS GENMASK(11, 0)
#define TSU_SSR 0x14
#define TSU_SSR_CONV BIT(0)
#define TSU_CMSR 0x18
#define TSU_CMSR_CMPEN BIT(0)
#define TSU_LLSR 0x1C
#define TSU_ULSR 0x20
#define TSU_SISR 0x30
#define TSU_SISR_ADF BIT(0)
#define TSU_SISR_CMPF BIT(1)
#define TSU_SIER 0x34
#define TSU_SIER_CMPIE BIT(1)
#define TSU_SICR 0x38
#define TSU_SICR_ADCLR BIT(0)
#define TSU_SICR_CMPCLR BIT(1)
/* Temperature calculation constants from datasheet */
#define TSU_TEMP_D (-41)
#define TSU_TEMP_E 126
#define TSU_CODE_MAX 0xFFF
/* Timing specifications from datasheet */
#define TSU_POWERUP_TIME_US 120 /* 120T at 1MHz sensor clock per datasheet */
#define TSU_CONV_TIME_US 50 /* Per sample conversion time */
#define TSU_POLL_DELAY_US 10 /* Polling interval */
#define TSU_MIN_CLOCK_RATE 24000000 /* TSU_PCLK minimum 24MHz */
/**
* struct rzg3e_thermal_priv - RZ/G3E TSU private data
* @base: TSU register base
* @dev: device pointer
* @syscon: regmap for calibration values
* @zone: thermal zone device
* @rstc: reset control
* @trmval0: calibration value 0 (b)
* @trmval1: calibration value 1 (c)
* @trim_offset: offset for trim registers in syscon
* @lock: protects hardware access during conversions
*/
struct rzg3e_thermal_priv {
void __iomem *base;
struct device *dev;
struct regmap *syscon;
struct thermal_zone_device *zone;
struct reset_control *rstc;
u16 trmval0;
u16 trmval1;
u32 trim_offset;
struct mutex lock;
};
static int rzg3e_thermal_power_on(struct rzg3e_thermal_priv *priv)
{
u32 val;
int ret;
/* Clear any pending interrupts */
writel(TSU_SICR_ADCLR | TSU_SICR_CMPCLR, priv->base + TSU_SICR);
/* Disable all interrupts during setup */
writel(0, priv->base + TSU_SIER);
/*
* Power-on sequence per datasheet 7.11.9.1:
* SOC_TS_EN must be set at same time or before EN_TS and ADC_PD_TS
*/
val = TSU_SSUSR_SOC_TS_EN | TSU_SSUSR_EN_TS;
writel(val, priv->base + TSU_SSUSR);
/* Wait for sensor stabilization per datasheet 7.11.7.1 */
usleep_range(TSU_POWERUP_TIME_US, TSU_POWERUP_TIME_US + 10);
/* Configure for average mode with 8 samples */
val = TSU_SOSR1_OUTSEL | TSU_SOSR1_ADCT_8;
writel(val, priv->base + TSU_SOSR1);
/* Ensure we're in single scan mode (default) */
val = readl(priv->base + TSU_SOSR1);
if (val & TSU_SOSR1_ADCS) {
dev_err(priv->dev, "Invalid scan mode setting\n");
return -EINVAL;
}
/* Wait for any ongoing conversion to complete */
ret = readl_poll_timeout(priv->base + TSU_SSR, val,
!(val & TSU_SSR_CONV),
TSU_POLL_DELAY_US,
USEC_PER_MSEC);
if (ret) {
dev_err(priv->dev, "Timeout waiting for conversion\n");
return ret;
}
return 0;
}
static void rzg3e_thermal_power_off(struct rzg3e_thermal_priv *priv)
{
/* Disable all interrupts */
writel(0, priv->base + TSU_SIER);
/* Clear pending interrupts */
writel(TSU_SICR_ADCLR | TSU_SICR_CMPCLR, priv->base + TSU_SICR);
/* Power down sequence per datasheet */
writel(TSU_SSUSR_ADC_PD_TS, priv->base + TSU_SSUSR);
}
/*
* Convert 12-bit sensor code to temperature in millicelsius
* Formula from datasheet 7.11.7.8:
* T(°C) = ((e - d) / (c - b)) * (a - b) + d
* where: a = sensor code, b = trmval0, c = trmval1, d = -41, e = 126
*/
static int rzg3e_thermal_code_to_temp(struct rzg3e_thermal_priv *priv, u16 code)
{
int temp_e_mc = TSU_TEMP_E * MILLIDEGREE_PER_DEGREE;
int temp_d_mc = TSU_TEMP_D * MILLIDEGREE_PER_DEGREE;
s64 numerator, denominator;
int temp_mc;
numerator = (temp_e_mc - temp_d_mc) * (s64)(code - priv->trmval0);
denominator = priv->trmval1 - priv->trmval0;
temp_mc = div64_s64(numerator, denominator) + temp_d_mc;
return clamp(temp_mc, temp_d_mc, temp_e_mc);
}
/*
* Convert temperature in millicelsius to 12-bit sensor code
* Formula from datasheet 7.11.7.9 (inverse of above)
*/
static u16 rzg3e_thermal_temp_to_code(struct rzg3e_thermal_priv *priv, int temp_mc)
{
int temp_e_mc = TSU_TEMP_E * MILLIDEGREE_PER_DEGREE;
int temp_d_mc = TSU_TEMP_D * MILLIDEGREE_PER_DEGREE;
s64 numerator, denominator;
s64 code;
numerator = (temp_mc - temp_d_mc) * (priv->trmval1 - priv->trmval0);
denominator = temp_e_mc - temp_d_mc;
code = div64_s64(numerator, denominator) + priv->trmval0;
return clamp_val(code, 0, TSU_CODE_MAX);
}
static int rzg3e_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct rzg3e_thermal_priv *priv = thermal_zone_device_priv(tz);
u32 status, code;
int ret, timeout;
ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0)
return ret;
guard(mutex)(&priv->lock);
/* Clear any previous conversion status */
writel(TSU_SICR_ADCLR, priv->base + TSU_SICR);
/* Start single conversion */
writel(TSU_STRGR_ADST, priv->base + TSU_STRGR);
/* Wait for conversion completion - 8 samples at ~50us each */
timeout = TSU_CONV_TIME_US * 8 * 2; /* Double for margin */
ret = readl_poll_timeout(priv->base + TSU_SISR, status,
status & TSU_SISR_ADF,
TSU_POLL_DELAY_US, timeout);
if (ret) {
dev_err(priv->dev, "Conversion timeout (status=0x%08x)\n", status);
goto out;
}
/* Read the averaged result and clear the complete flag */
code = readl(priv->base + TSU_SCRR) & TSU_SCRR_OUT12BIT_TS;
writel(TSU_SICR_ADCLR, priv->base + TSU_SICR);
/* Convert to temperature */
*temp = rzg3e_thermal_code_to_temp(priv, code);
dev_dbg(priv->dev, "temp=%d mC (%d.%03d°C), code=0x%03x\n",
*temp, *temp / 1000, abs(*temp) % 1000, code);
out:
pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return ret;
}
static int rzg3e_thermal_set_trips(struct thermal_zone_device *tz,
int low, int high)
{
struct rzg3e_thermal_priv *priv = thermal_zone_device_priv(tz);
u16 low_code, high_code;
u32 val;
int ret;
/* Hardware requires low < high */
if (low >= high)
return -EINVAL;
ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0)
return ret;
guard(mutex)(&priv->lock);
/* Convert temperatures to codes */
low_code = rzg3e_thermal_temp_to_code(priv, low);
high_code = rzg3e_thermal_temp_to_code(priv, high);
dev_dbg(priv->dev, "set_trips: low=%d high=%d (codes: 0x%03x/0x%03x)\n",
low, high, low_code, high_code);
/* Disable comparison during reconfiguration */
writel(0, priv->base + TSU_SIER);
writel(0, priv->base + TSU_CMSR);
/* Clear any pending comparison interrupts */
writel(TSU_SICR_CMPCLR, priv->base + TSU_SICR);
/* Set trip points */
writel(low_code, priv->base + TSU_LLSR);
writel(high_code, priv->base + TSU_ULSR);
/*
* Ensure OUTSEL is set for comparison per datasheet 7.11.7.4
* Comparison uses averaged data
*/
val = readl(priv->base + TSU_SOSR1);
val |= TSU_SOSR1_OUTSEL;
writel(val, priv->base + TSU_SOSR1);
/* Enable comparison with "out of range" mode (CMPCOND=0) */
writel(TSU_CMSR_CMPEN, priv->base + TSU_CMSR);
/* Unmask compare IRQ and start a conversion to evaluate window */
writel(TSU_SIER_CMPIE, priv->base + TSU_SIER);
writel(TSU_STRGR_ADST, priv->base + TSU_STRGR);
pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return 0;
}
static irqreturn_t rzg3e_thermal_irq_thread(int irq, void *data)
{
struct rzg3e_thermal_priv *priv = data;
dev_dbg(priv->dev, "Temperature threshold crossed\n");
/* Notify thermal framework to re-evaluate trip points */
thermal_zone_device_update(priv->zone, THERMAL_TRIP_VIOLATED);
return IRQ_HANDLED;
}
static irqreturn_t rzg3e_thermal_irq(int irq, void *data)
{
struct rzg3e_thermal_priv *priv = data;
u32 status;
status = readl(priv->base + TSU_SISR);
/* Check if comparison interrupt occurred */
if (status & TSU_SISR_CMPF) {
/* Clear irq flag and disable interrupt until reconfigured */
writel(TSU_SICR_CMPCLR, priv->base + TSU_SICR);
writel(0, priv->base + TSU_SIER);
return IRQ_WAKE_THREAD;
}
return IRQ_NONE;
}
static const struct thermal_zone_device_ops rzg3e_tz_ops = {
.get_temp = rzg3e_thermal_get_temp,
.set_trips = rzg3e_thermal_set_trips,
};
static int rzg3e_thermal_get_calibration(struct rzg3e_thermal_priv *priv)
{
u32 val;
int ret;
/* Read calibration values from syscon */
ret = regmap_read(priv->syscon, priv->trim_offset, &val);
if (ret)
return ret;
priv->trmval0 = val & GENMASK(11, 0);
ret = regmap_read(priv->syscon, priv->trim_offset + 4, &val);
if (ret)
return ret;
priv->trmval1 = val & GENMASK(11, 0);
/* Validate calibration data */
if (!priv->trmval0 || !priv->trmval1 ||
priv->trmval0 == priv->trmval1 ||
priv->trmval0 == 0xFFF || priv->trmval1 == 0xFFF) {
dev_err(priv->dev, "Invalid calibration: b=0x%03x, c=0x%03x\n",
priv->trmval0, priv->trmval1);
return -EINVAL;
}
dev_dbg(priv->dev, "Calibration: b=0x%03x (%u), c=0x%03x (%u)\n",
priv->trmval0, priv->trmval0, priv->trmval1, priv->trmval1);
return 0;
}
static int rzg3e_thermal_parse_dt(struct rzg3e_thermal_priv *priv)
{
struct device_node *np = priv->dev->of_node;
u32 offset;
priv->syscon = syscon_regmap_lookup_by_phandle_args(np, "renesas,tsu-trim", 1, &offset);
if (IS_ERR(priv->syscon))
return dev_err_probe(priv->dev, PTR_ERR(priv->syscon),
"Failed to parse renesas,tsu-trim\n");
priv->trim_offset = offset;
return 0;
}
static int rzg3e_thermal_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rzg3e_thermal_priv *priv;
struct clk *clk;
int irq, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
ret = devm_mutex_init(dev, &priv->lock);
if (ret)
return ret;
platform_set_drvdata(pdev, priv);
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
/* Parse device tree for trim register info */
ret = rzg3e_thermal_parse_dt(priv);
if (ret)
return ret;
/* Get clock to verify frequency - clock is managed by power domain */
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk),
"Failed to get clock\n");
if (clk_get_rate(clk) < TSU_MIN_CLOCK_RATE)
return dev_err_probe(dev, -EINVAL,
"Clock rate %lu Hz too low (min %u Hz)\n",
clk_get_rate(clk), TSU_MIN_CLOCK_RATE);
priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL);
if (IS_ERR(priv->rstc))
return dev_err_probe(dev, PTR_ERR(priv->rstc),
"Failed to get/deassert reset control\n");
/* Get calibration data */
ret = rzg3e_thermal_get_calibration(priv);
if (ret)
return dev_err_probe(dev, ret,
"Failed to get valid calibration data\n");
/* Get comparison interrupt */
irq = platform_get_irq_byname(pdev, "adcmpi");
if (irq < 0)
return irq;
/* Enable runtime PM */
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
devm_pm_runtime_enable(dev);
/* Initial hardware setup */
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return dev_err_probe(dev, ret, "Runtime resume failed\n");
/* Register thermal zone - this will trigger DT parsing */
priv->zone = devm_thermal_of_zone_register(dev, 0, priv, &rzg3e_tz_ops);
if (IS_ERR(priv->zone)) {
ret = PTR_ERR(priv->zone);
dev_err(dev, "Failed to register thermal zone: %d\n", ret);
goto err_pm_put;
}
/* Request threaded IRQ for comparison interrupt */
ret = devm_request_threaded_irq(dev, irq, rzg3e_thermal_irq,
rzg3e_thermal_irq_thread,
IRQF_ONESHOT, "rzg3e_thermal", priv);
if (ret) {
dev_err(dev, "Failed to request IRQ: %d\n", ret);
goto err_pm_put;
}
/* Add hwmon sysfs interface */
ret = devm_thermal_add_hwmon_sysfs(dev, priv->zone);
if (ret)
dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
dev_info(dev, "RZ/G3E thermal sensor registered\n");
return 0;
err_pm_put:
pm_runtime_put_sync(dev);
return ret;
}
static int rzg3e_thermal_runtime_suspend(struct device *dev)
{
struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
rzg3e_thermal_power_off(priv);
return 0;
}
static int rzg3e_thermal_runtime_resume(struct device *dev)
{
struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
return rzg3e_thermal_power_on(priv);
}
static int rzg3e_thermal_suspend(struct device *dev)
{
struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
/* If device is active, power it off */
if (pm_runtime_active(dev))
rzg3e_thermal_power_off(priv);
/* Assert reset to ensure clean state after resume */
reset_control_assert(priv->rstc);
return 0;
}
static int rzg3e_thermal_resume(struct device *dev)
{
struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
int ret;
/* Deassert reset */
ret = reset_control_deassert(priv->rstc);
if (ret) {
dev_err(dev, "Failed to deassert reset: %d\n", ret);
return ret;
}
/* If device was active before suspend, power it back on */
if (pm_runtime_active(dev))
return rzg3e_thermal_power_on(priv);
return 0;
}
static const struct dev_pm_ops rzg3e_thermal_pm_ops = {
RUNTIME_PM_OPS(rzg3e_thermal_runtime_suspend,
rzg3e_thermal_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(rzg3e_thermal_suspend, rzg3e_thermal_resume)
};
static const struct of_device_id rzg3e_thermal_dt_ids[] = {
{ .compatible = "renesas,r9a09g047-tsu" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg3e_thermal_dt_ids);
static struct platform_driver rzg3e_thermal_driver = {
.driver = {
.name = "rzg3e_thermal",
.of_match_table = rzg3e_thermal_dt_ids,
.pm = pm_ptr(&rzg3e_thermal_pm_ops),
},
.probe = rzg3e_thermal_probe,
};
module_platform_driver(rzg3e_thermal_driver);
MODULE_DESCRIPTION("Renesas RZ/G3E TSU Thermal Sensor Driver");
MODULE_AUTHOR("John Madieu <john.madieu.xa@bp.renesas.com>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,272 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G3S TSU Thermal Sensor Driver
*
* Copyright (C) 2024 Renesas Electronics Corporation
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/iio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/thermal.h>
#include <linux/units.h>
#include "../thermal_hwmon.h"
#define TSU_SM 0x0
#define TSU_SM_EN BIT(0)
#define TSU_SM_OE BIT(1)
#define OTPTSUTRIM_REG(n) (0x18 + (n) * 0x4)
#define OTPTSUTRIM_EN_MASK BIT(31)
#define OTPTSUTRIM_MASK GENMASK(11, 0)
#define TSU_READ_STEPS 8
/* Default calibration values, if FUSE values are missing. */
#define SW_CALIB0_VAL 1297
#define SW_CALIB1_VAL 751
#define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE)
/**
* struct rzg3s_thermal_priv - RZ/G3S thermal private data structure
* @base: TSU base address
* @dev: device pointer
* @tz: thermal zone pointer
* @rstc: reset control
* @channel: IIO channel to read the TSU
* @mode: current device mode
* @calib0: calibration value
* @calib1: calibration value
*/
struct rzg3s_thermal_priv {
void __iomem *base;
struct device *dev;
struct thermal_zone_device *tz;
struct reset_control *rstc;
struct iio_channel *channel;
enum thermal_device_mode mode;
u16 calib0;
u16 calib1;
};
static int rzg3s_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
int ts_code_ave = 0;
if (priv->mode != THERMAL_DEVICE_ENABLED)
return -EAGAIN;
for (u8 i = 0; i < TSU_READ_STEPS; i++) {
int ret, val;
ret = iio_read_channel_raw(priv->channel, &val);
if (ret < 0)
return ret;
ts_code_ave += val;
/*
* According to the HW manual (Rev.1.10, section 40.4.4 Procedure for Measuring
* the Temperature) we need to wait here at leat 3us.
*/
usleep_range(5, 10);
}
ts_code_ave = DIV_ROUND_CLOSEST(MCELSIUS(ts_code_ave), TSU_READ_STEPS);
/*
* According to the HW manual (Rev.1.10, section 40.4.4 Procedure for Measuring the
* Temperature) the computation formula is as follows:
*
* Tj = (ts_code_ave - priv->calib1) * 165 / (priv->calib0 - priv->calib1) - 40
*
* Convert everything to milli Celsius before applying the formula to avoid
* losing precision.
*/
*temp = div_s64((s64)(ts_code_ave - MCELSIUS(priv->calib1)) * MCELSIUS(165),
MCELSIUS(priv->calib0 - priv->calib1)) - MCELSIUS(40);
/* Report it in milli degrees Celsius and round it up to 0.5 degrees Celsius. */
*temp = roundup(*temp, 500);
return 0;
}
static void rzg3s_thermal_set_mode(struct rzg3s_thermal_priv *priv,
enum thermal_device_mode mode)
{
struct device *dev = priv->dev;
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret)
return;
if (mode == THERMAL_DEVICE_DISABLED) {
writel(0, priv->base + TSU_SM);
} else {
writel(TSU_SM_EN, priv->base + TSU_SM);
/*
* According to the HW manual (Rev.1.10, section 40.4.1 Procedure for
* Starting the TSU) we need to wait here 30us or more.
*/
usleep_range(30, 40);
writel(TSU_SM_OE | TSU_SM_EN, priv->base + TSU_SM);
/*
* According to the HW manual (Rev.1.10, section 40.4.1 Procedure for
* Starting the TSU) we need to wait here 50us or more.
*/
usleep_range(50, 60);
}
pm_runtime_put_autosuspend(dev);
}
static int rzg3s_thermal_change_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode)
{
struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
if (priv->mode == mode)
return 0;
rzg3s_thermal_set_mode(priv, mode);
priv->mode = mode;
return 0;
}
static const struct thermal_zone_device_ops rzg3s_tz_of_ops = {
.get_temp = rzg3s_thermal_get_temp,
.change_mode = rzg3s_thermal_change_mode,
};
static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv)
{
struct device *dev = priv->dev;
u32 val;
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
val = readl(priv->base + OTPTSUTRIM_REG(0));
if (val & OTPTSUTRIM_EN_MASK)
priv->calib0 = FIELD_GET(OTPTSUTRIM_MASK, val);
else
priv->calib0 = SW_CALIB0_VAL;
val = readl(priv->base + OTPTSUTRIM_REG(1));
if (val & OTPTSUTRIM_EN_MASK)
priv->calib1 = FIELD_GET(OTPTSUTRIM_MASK, val);
else
priv->calib1 = SW_CALIB1_VAL;
pm_runtime_put_autosuspend(dev);
return 0;
}
static int rzg3s_thermal_probe(struct platform_device *pdev)
{
struct rzg3s_thermal_priv *priv;
struct device *dev = &pdev->dev;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->channel = devm_iio_channel_get(dev, "tsu");
if (IS_ERR(priv->channel))
return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n");
priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL);
if (IS_ERR(priv->rstc))
return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n");
priv->dev = dev;
priv->mode = THERMAL_DEVICE_DISABLED;
platform_set_drvdata(pdev, priv);
pm_runtime_set_autosuspend_delay(dev, 300);
pm_runtime_use_autosuspend(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable runtime PM!\n");
ret = rzg3s_thermal_read_calib(priv);
if (ret)
return dev_err_probe(dev, ret, "Failed to read calibration data!\n");
priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &rzg3s_tz_of_ops);
if (IS_ERR(priv->tz))
return dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n");
ret = devm_thermal_add_hwmon_sysfs(dev, priv->tz);
if (ret)
return dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n");
return 0;
}
static int rzg3s_thermal_suspend(struct device *dev)
{
struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev);
rzg3s_thermal_set_mode(priv, THERMAL_DEVICE_DISABLED);
return reset_control_assert(priv->rstc);
}
static int rzg3s_thermal_resume(struct device *dev)
{
struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev);
int ret;
ret = reset_control_deassert(priv->rstc);
if (ret)
return ret;
if (priv->mode != THERMAL_DEVICE_DISABLED)
rzg3s_thermal_set_mode(priv, priv->mode);
return 0;
}
static const struct dev_pm_ops rzg3s_thermal_pm_ops = {
SYSTEM_SLEEP_PM_OPS(rzg3s_thermal_suspend, rzg3s_thermal_resume)
};
static const struct of_device_id rzg3s_thermal_dt_ids[] = {
{ .compatible = "renesas,r9a08g045-tsu" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg3s_thermal_dt_ids);
static struct platform_driver rzg3s_thermal_driver = {
.driver = {
.name = "rzg3s-thermal",
.of_match_table = rzg3s_thermal_dt_ids,
.pm = pm_ptr(&rzg3s_thermal_pm_ops),
},
.probe = rzg3s_thermal_probe,
};
module_platform_driver(rzg3s_thermal_driver);
MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver");
MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
MODULE_LICENSE("GPL");

View File

@@ -74,6 +74,7 @@ struct chip_tsadc_table {
* @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
* @grf_required: true, if a GRF is required for proper functionality
* @initialize: SoC special initialize tsadc controller method
* @irq_ack: clear the interrupt
* @control: enable/disable method for the tsadc controller
@@ -97,6 +98,9 @@ struct rockchip_tsadc_chip {
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
/* GRF availability */
bool grf_required;
/* Chip-wide methods */
void (*initialize)(struct regmap *grf,
void __iomem *reg, enum tshut_polarity p);
@@ -1098,10 +1102,9 @@ static const struct rockchip_tsadc_chip px30_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* 2 channels for tsadc */
.grf_required = true,
.tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
.tshut_temp = 95000,
.initialize = rk_tsadcv4_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1109,7 +1112,6 @@ static const struct rockchip_tsadc_chip px30_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3328_code_table,
.length = ARRAY_SIZE(rk3328_code_table),
@@ -1122,11 +1124,10 @@ static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
/* cpu */
.chn_offset = 0,
.chn_num = 1, /* one channel for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1134,7 +1135,6 @@ static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rv1108_table,
.length = ARRAY_SIZE(rv1108_table),
@@ -1147,11 +1147,10 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
/* cpu */
.chn_offset = 0,
.chn_num = 1, /* one channel for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1159,7 +1158,6 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3228_code_table,
.length = ARRAY_SIZE(rk3228_code_table),
@@ -1172,11 +1170,10 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
/* cpu, gpu */
.chn_offset = 1,
.chn_num = 2, /* two channels for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv2_irq_ack,
.control = rk_tsadcv2_control,
@@ -1184,7 +1181,6 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3288_code_table,
.length = ARRAY_SIZE(rk3288_code_table),
@@ -1197,10 +1193,9 @@ static const struct rockchip_tsadc_chip rk3328_tsadc_data = {
/* cpu */
.chn_offset = 0,
.chn_num = 1, /* one channels for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
.tshut_temp = 95000,
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1208,7 +1203,6 @@ static const struct rockchip_tsadc_chip rk3328_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3328_code_table,
.length = ARRAY_SIZE(rk3328_code_table),
@@ -1221,11 +1215,10 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
.grf_required = true,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv3_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1233,7 +1226,6 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3228_code_table,
.length = ARRAY_SIZE(rk3228_code_table),
@@ -1246,11 +1238,10 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv2_irq_ack,
.control = rk_tsadcv2_control,
@@ -1258,7 +1249,6 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3368_code_table,
.length = ARRAY_SIZE(rk3368_code_table),
@@ -1271,11 +1261,10 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
.grf_required = true,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv3_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1283,7 +1272,6 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3399_code_table,
.length = ARRAY_SIZE(rk3399_code_table),
@@ -1296,11 +1284,10 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
.grf_required = true,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
.initialize = rk_tsadcv7_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1308,7 +1295,6 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.table = {
.id = rk3568_code_table,
.length = ARRAY_SIZE(rk3568_code_table),
@@ -1321,6 +1307,7 @@ static const struct rockchip_tsadc_chip rk3576_tsadc_data = {
/* top, big_core, little_core, ddr, npu, gpu */
.chn_offset = 0,
.chn_num = 6, /* six channels for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
@@ -1345,6 +1332,7 @@ static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
/* top, big_core0, big_core1, little_core, center, gpu, npu */
.chn_offset = 0,
.chn_num = 7, /* seven channels for tsadc */
.grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
@@ -1621,12 +1609,10 @@ static int rockchip_configure_from_dt(struct device *dev,
return -EINVAL;
}
/* The tsadc wont to handle the error in here since some SoCs didn't
* need this property.
*/
thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(thermal->grf))
dev_warn(dev, "Missing rockchip,grf property\n");
if (IS_ERR(thermal->grf) && thermal->chip->grf_required)
return dev_err_probe(dev, PTR_ERR(thermal->grf),
"Missing rockchip,grf property\n");
rockchip_get_trim_configuration(dev, np, thermal);

View File

@@ -4,6 +4,7 @@ obj-$(CONFIG_TEGRA_BPMP_THERMAL) += tegra-bpmp-thermal.o
obj-$(CONFIG_TEGRA30_TSENSOR) += tegra30-tsensor.o
tegra-soctherm-y := soctherm.o soctherm-fuse.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-soctherm.o

View File

@@ -9,15 +9,12 @@
#include "soctherm.h"
#define NOMINAL_CALIB_FT 105
#define NOMINAL_CALIB_CP 25
#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
#define FUSE_TSENSOR_COMMON 0x180
/*
* Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
* 3 2 1 0
@@ -26,7 +23,7 @@
* | BASE_FT | BASE_CP | SHFT_FT | SHIFT_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Tegra12x, etc:
* Tegra124:
* In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
* and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
* were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
@@ -44,6 +41,13 @@
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |---------------------------------------------------| SHIFT_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Tegra114: Layout of bits in FUSE_TSENSOR_COMMON aka FUSE_VSENSOR_CALIB:
* 3 2 1 0
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SHFT_FT | BASE_FT | SHIFT_CP | BASE_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#define CALIB_COEFFICIENT 1000000LL
@@ -77,7 +81,7 @@ int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
s32 shifted_cp, shifted_ft;
int err;
err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
err = tegra_fuse_readl(tfuse->fuse_common_reg, &val);
if (err)
return err;
@@ -96,10 +100,12 @@ int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
return err;
}
shifted_cp = (val & tfuse->fuse_shift_cp_mask) >>
tfuse->fuse_shift_cp_shift;
shifted_cp = sign_extend32(val, 5);
shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
shared->actual_temp_ft = 2 * tfuse->nominal_calib_ft + shifted_ft;
return 0;
}

View File

@@ -31,6 +31,7 @@
#include <linux/reset.h>
#include <linux/thermal.h>
#include <dt-bindings/thermal/tegra114-soctherm.h>
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include "../thermal_core.h"
@@ -357,6 +358,12 @@ struct soctherm_oc_irq_chip_data {
static struct soctherm_oc_irq_chip_data soc_irq_cdata;
/* Ensure that TEGRA114_* and TEGRA124_* counterparts are equal */
static_assert(TEGRA114_SOCTHERM_SENSOR_CPU == TEGRA124_SOCTHERM_SENSOR_CPU);
static_assert(TEGRA114_SOCTHERM_SENSOR_MEM == TEGRA124_SOCTHERM_SENSOR_MEM);
static_assert(TEGRA114_SOCTHERM_SENSOR_GPU == TEGRA124_SOCTHERM_SENSOR_GPU);
static_assert(TEGRA114_SOCTHERM_SENSOR_PLLX == TEGRA124_SOCTHERM_SENSOR_PLLX);
/**
* ccroc_writel() - writes a value to a CCROC register
* @ts: pointer to a struct tegra_soctherm
@@ -2045,6 +2052,12 @@ static void soctherm_init(struct platform_device *pdev)
}
static const struct of_device_id tegra_soctherm_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_114_SOC
{
.compatible = "nvidia,tegra114-soctherm",
.data = &tegra114_soctherm,
},
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
{
.compatible = "nvidia,tegra124-soctherm",

View File

@@ -56,6 +56,9 @@
#define SENSOR_TEMP2_MEM_TEMP_MASK (0xffff << 16)
#define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff
#define FUSE_VSENSOR_CALIB 0x08c
#define FUSE_TSENSOR_COMMON 0x180
/**
* struct tegra_tsensor_group - SOC_THERM sensor group data
* @name: short name of the temperature sensor group
@@ -109,9 +112,11 @@ struct tsensor_group_thermtrips {
struct tegra_soctherm_fuse {
u32 fuse_base_cp_mask, fuse_base_cp_shift;
u32 fuse_shift_cp_mask, fuse_shift_cp_shift;
u32 fuse_base_ft_mask, fuse_base_ft_shift;
u32 fuse_shift_ft_mask, fuse_shift_ft_shift;
u32 fuse_spare_realignment;
u32 fuse_common_reg, fuse_spare_realignment;
u32 nominal_calib_ft;
};
struct tsensor_shared_calib {
@@ -137,6 +142,10 @@ int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
const struct tsensor_shared_calib *shared,
u32 *calib);
#ifdef CONFIG_ARCH_TEGRA_114_SOC
extern const struct tegra_soctherm_soc tegra114_soctherm;
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_soctherm_soc tegra124_soctherm;
#endif

View File

@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2024, Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/thermal/tegra114-soctherm.h>
#include "soctherm.h"
#define TEGRA114_THERMTRIP_ANY_EN_MASK (0x1 << 28)
#define TEGRA114_THERMTRIP_MEM_EN_MASK (0x1 << 27)
#define TEGRA114_THERMTRIP_GPU_EN_MASK (0x1 << 26)
#define TEGRA114_THERMTRIP_CPU_EN_MASK (0x1 << 25)
#define TEGRA114_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
#define TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
#define TEGRA114_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
#define TEGRA114_THERMTRIP_TSENSE_THRESH_MASK 0xff
#define TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17)
#define TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9)
#define TEGRA114_THRESH_GRAIN 1000
#define TEGRA114_BPTT 8
static const struct tegra_tsensor_configuration tegra114_tsensor_config = {
.tall = 16300,
.tiddq_en = 1,
.ten_count = 1,
.tsample = 163,
.tsample_ate = 655,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_cpu = {
.id = TEGRA114_SOCTHERM_SENSOR_CPU,
.name = "cpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
.pllx_hotspot_diff = 6,
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_CPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_CPU_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_CPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_gpu = {
.id = TEGRA114_SOCTHERM_SENSOR_GPU,
.name = "gpu",
.sensor_temp_offset = SENSOR_TEMP1,
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
.pllx_hotspot_diff = 6,
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_GPU_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_GPU_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_pll = {
.id = TEGRA114_SOCTHERM_SENSOR_PLLX,
.name = "pll",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_TSENSE_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_TSENSE_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group tegra114_tsensor_group_mem = {
.id = TEGRA114_SOCTHERM_SENSOR_MEM,
.name = "mem",
.sensor_temp_offset = SENSOR_TEMP2,
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
.pdiv = 10,
.pdiv_ate = 10,
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
.pllx_hotspot_diff = 0,
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
.thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
.thermtrip_enable_mask = TEGRA114_THERMTRIP_MEM_EN_MASK,
.thermtrip_threshold_mask = TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK,
.thermctl_isr_mask = THERM_IRQ_MEM_MASK,
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
.thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
.thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
};
static const struct tegra_tsensor_group *tegra114_tsensor_groups[] = {
&tegra114_tsensor_group_cpu,
&tegra114_tsensor_group_gpu,
&tegra114_tsensor_group_pll,
&tegra114_tsensor_group_mem,
};
static const struct tegra_tsensor tegra114_tsensors[] = {
{
.name = "cpu0",
.base = 0xc0,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x098,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "cpu1",
.base = 0xe0,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x084,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "cpu2",
.base = 0x100,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x088,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "cpu3",
.base = 0x120,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x12c,
.fuse_corr_alpha = 1196400,
.fuse_corr_beta = -13600000,
.group = &tegra114_tsensor_group_cpu,
}, {
.name = "mem0",
.base = 0x140,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x158,
.fuse_corr_alpha = 1000000,
.fuse_corr_beta = 0,
.group = &tegra114_tsensor_group_mem,
}, {
.name = "mem1",
.base = 0x160,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x15c,
.fuse_corr_alpha = 1000000,
.fuse_corr_beta = 0,
.group = &tegra114_tsensor_group_mem,
}, {
.name = "gpu",
.base = 0x180,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x154,
.fuse_corr_alpha = 1124500,
.fuse_corr_beta = -9793100,
.group = &tegra114_tsensor_group_gpu,
}, {
.name = "pllx",
.base = 0x1a0,
.config = &tegra114_tsensor_config,
.calib_fuse_offset = 0x160,
.fuse_corr_alpha = 1224200,
.fuse_corr_beta = -14665000,
.group = &tegra114_tsensor_group_pll,
},
};
static const struct tegra_soctherm_fuse tegra114_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_shift_cp_mask = 0x3f << 10,
.fuse_shift_cp_shift = 10,
.fuse_base_ft_mask = 0x7ff << 16,
.fuse_base_ft_shift = 16,
.fuse_shift_ft_mask = 0x1f << 27,
.fuse_shift_ft_shift = 27,
.fuse_common_reg = FUSE_VSENSOR_CALIB,
.fuse_spare_realignment = 0,
.nominal_calib_ft = 90,
};
const struct tegra_soctherm_soc tegra114_soctherm = {
.tsensors = tegra114_tsensors,
.num_tsensors = ARRAY_SIZE(tegra114_tsensors),
.ttgs = tegra114_tsensor_groups,
.num_ttgs = ARRAY_SIZE(tegra114_tsensor_groups),
.tfuse = &tegra114_soctherm_fuse,
.thresh_grain = TEGRA114_THRESH_GRAIN,
.bptt = TEGRA114_BPTT,
.use_ccroc = false,
};

View File

@@ -200,11 +200,15 @@ static const struct tegra_tsensor tegra124_tsensors[] = {
static const struct tegra_soctherm_fuse tegra124_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_shift_cp_mask = 0x3f,
.fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
.fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0x1fc,
.nominal_calib_ft = 105,
};
const struct tegra_soctherm_soc tegra124_soctherm = {

View File

@@ -200,11 +200,15 @@ static struct tegra_tsensor tegra132_tsensors[] = {
static const struct tegra_soctherm_fuse tegra132_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
.fuse_shift_cp_mask = 0x3f,
.fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
.fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0x1fc,
.nominal_calib_ft = 105,
};
const struct tegra_soctherm_soc tegra132_soctherm = {

View File

@@ -201,11 +201,15 @@ static const struct tegra_tsensor tegra210_tsensors[] = {
static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff << 11,
.fuse_base_cp_shift = 11,
.fuse_shift_cp_mask = 0x3f,
.fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 21,
.fuse_base_ft_shift = 21,
.fuse_shift_ft_mask = 0x1f << 6,
.fuse_shift_ft_shift = 6,
.fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0,
.nominal_calib_ft = 105,
};
static struct tsensor_group_thermtrips tegra210_tsensor_thermtrips[] = {

View File

@@ -184,15 +184,14 @@ static void tt_add_tz_work_fn(struct work_struct *work)
int tt_add_tz(void)
{
struct tt_thermal_zone *tt_zone __free(kfree);
struct tt_work *tt_work __free(kfree) = NULL;
int ret;
tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL);
struct tt_thermal_zone *tt_zone __free(kfree) = kzalloc(sizeof(*tt_zone),
GFP_KERNEL);
if (!tt_zone)
return -ENOMEM;
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL);
if (!tt_work)
return -ENOMEM;
@@ -237,7 +236,6 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone)
int tt_del_tz(const char *arg)
{
struct tt_work *tt_work __free(kfree) = NULL;
struct tt_thermal_zone *tt_zone, *aux;
int ret;
int id;
@@ -246,7 +244,7 @@ int tt_del_tz(const char *arg)
if (ret != 1)
return -EINVAL;
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL);
if (!tt_work)
return -ENOMEM;
@@ -330,20 +328,17 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work)
int tt_zone_add_trip(const char *arg)
{
struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL;
struct tt_trip *tt_trip __free(kfree) = NULL;
struct tt_work *tt_work __free(kfree);
int id;
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL);
if (!tt_work)
return -ENOMEM;
tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL);
struct tt_trip *tt_trip __free(kfree) = kzalloc(sizeof(*tt_trip), GFP_KERNEL);
if (!tt_trip)
return -ENOMEM;
tt_zone = tt_get_tt_zone(arg);
struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg);
if (IS_ERR(tt_zone))
return PTR_ERR(tt_zone);
@@ -387,7 +382,6 @@ static const struct thermal_zone_device_ops tt_zone_ops = {
static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
{
struct thermal_trip *trips __free(kfree) = NULL;
struct thermal_zone_device *tz;
struct tt_trip *tt_trip;
int i;
@@ -397,7 +391,8 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
if (tt_zone->tz)
return -EINVAL;
trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL);
struct thermal_trip *trips __free(kfree) = kcalloc(tt_zone->num_trips,
sizeof(*trips), GFP_KERNEL);
if (!trips)
return -ENOMEM;
@@ -421,9 +416,7 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
int tt_zone_reg(const char *arg)
{
struct tt_thermal_zone *tt_zone __free(put_tt_zone);
tt_zone = tt_get_tt_zone(arg);
struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg);
if (IS_ERR(tt_zone))
return PTR_ERR(tt_zone);
@@ -432,9 +425,7 @@ int tt_zone_reg(const char *arg)
int tt_zone_unreg(const char *arg)
{
struct tt_thermal_zone *tt_zone __free(put_tt_zone);
tt_zone = tt_get_tt_zone(arg);
struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg);
if (IS_ERR(tt_zone))
return PTR_ERR(tt_zone);

View File

@@ -7,6 +7,7 @@
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*/
#include <linux/iio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -73,6 +74,58 @@ static const struct thermal_zone_device_ops gadc_thermal_ops = {
.get_temp = gadc_thermal_get_temp,
};
static const struct iio_chan_spec gadc_thermal_iio_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
}
};
static int gadc_thermal_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct gadc_thermal_info *gtinfo = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
ret = gadc_thermal_get_temp(gtinfo->tz_dev, val);
if (ret)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_info gadc_thermal_iio_info = {
.read_raw = gadc_thermal_read_raw,
};
static int gadc_iio_register(struct device *dev, struct gadc_thermal_info *gti)
{
struct gadc_thermal_info *gtinfo;
struct iio_dev *indio_dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*gtinfo));
if (!indio_dev)
return -ENOMEM;
gtinfo = iio_priv(indio_dev);
memcpy(gtinfo, gti, sizeof(*gtinfo));
indio_dev->name = dev_name(dev);
indio_dev->info = &gadc_thermal_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = gadc_thermal_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(gadc_thermal_iio_channels);
return devm_iio_device_register(dev, indio_dev);
}
static int gadc_thermal_read_linear_lookup_table(struct device *dev,
struct gadc_thermal_info *gti)
{
@@ -153,7 +206,7 @@ static int gadc_thermal_probe(struct platform_device *pdev)
devm_thermal_add_hwmon_sysfs(dev, gti->tz_dev);
return 0;
return gadc_iio_register(&pdev->dev, gti);
}
static const struct of_device_id of_adc_thermal_match[] = {

View File

@@ -96,7 +96,7 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
mutex_lock(&thermal_hwmon_list_lock);
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
strcpy(type, tz->type);
strscpy(type, tz->type);
strreplace(type, '-', '_');
if (!strcmp(hwmon->type, type)) {
mutex_unlock(&thermal_hwmon_list_lock);

View File

@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* This header provides constants for binding nvidia,tegra114-soctherm.
*/
#ifndef _DT_BINDINGS_THERMAL_TEGRA114_SOCTHERM_H
#define _DT_BINDINGS_THERMAL_TEGRA114_SOCTHERM_H
#define TEGRA114_SOCTHERM_SENSOR_CPU 0
#define TEGRA114_SOCTHERM_SENSOR_MEM 1
#define TEGRA114_SOCTHERM_SENSOR_GPU 2
#define TEGRA114_SOCTHERM_SENSOR_PLLX 3
#define TEGRA114_SOCTHERM_THROT_LEVEL_NONE 0
#define TEGRA114_SOCTHERM_THROT_LEVEL_LOW 1
#define TEGRA114_SOCTHERM_THROT_LEVEL_MED 2
#define TEGRA114_SOCTHERM_THROT_LEVEL_HIGH 3
#endif

View File

@@ -144,6 +144,8 @@ int main(int argc, char **argv)
ret = sscanf(index_str, "%d", &index);
if (ret < 0)
break;
index &= 0x0f;
if (index > WORKLOAD_TYPE_MAX_INDEX)
printf("Invalid workload type index\n");
else