mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
@@ -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:
|
||||
|
||||
14
MAINTAINERS
14
MAINTAINERS
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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");
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
547
drivers/thermal/renesas/rzg3e_thermal.c
Normal file
547
drivers/thermal/renesas/rzg3e_thermal.c
Normal 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");
|
||||
272
drivers/thermal/renesas/rzg3s_thermal.c
Normal file
272
drivers/thermal/renesas/rzg3s_thermal.c
Normal 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");
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
209
drivers/thermal/tegra/tegra114-soctherm.c
Normal file
209
drivers/thermal/tegra/tegra114-soctherm.c
Normal 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,
|
||||
};
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
19
include/dt-bindings/thermal/tegra114-soctherm.h
Normal file
19
include/dt-bindings/thermal/tegra114-soctherm.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user