From: Balsam CHIHI <[email protected]>
The LVTS (Low Voltage Thermal Sensor) driver is capable of monitoring
multiple hot points. For that, it contains 7 thermal control blocks
dedicated to specific devices on the die. Each control block can handle
up to 4 sensors.
The thermal controller supports several interrupts. One for the cold
trip point, the hot trip point, the return to the normal trip point,
and a specific programmable trip point to monitor the temperature
dynamically.
The temperature measurement can be done in two ways, the immediate mode
where the temperature read is instantaneous and the filtered mode where
the controller uses, by configuration, an average of a set of values
removing the minimum and the maximum.
Finally, it is composed of 2 finite-state machines responsible for
the state of the temperature (cold, hot, hot 2 normal, hot hot),
the triggering of the interrupts, and the monitoring of the temperature.
As requested, the thermal driver has been reworked to reduce
the complexity of the code. At this time, the 4 little CPUs and
the 4 big CPUs are supported by the thermal driver.They are described
in a data structure and more devices can be added later.
The calibration routine has been simplified also.
The series provide the following changes:
- Move the Mediatek drivers inside a dedicated folder as their number
is increasing
- Add the DT bindings for the controller
- Add the efuse node for the mt8195
- The LVTS driver
- The thermal zones description in the DT
Changelog:
v11:
- Rebase on top of "thermal/linux-next" :
base=0d568e144ead70189e7f16066dcb155b78ff9266
- Remove unsupported SoC (mt8192) from dt-binding definition
- Fix coding style issues :
- Move litterals to define
- Add interrupt macros
- Remove wildcard : only mt8195 is supported for now
v10:
- Rebase on top of "thermal/linux-next" : thermal-v6.3-rc1
- Rework the LVTS driver
- Add the thermal trip temperature and cooling devices
for the sensors supported by the driver
v9:
- Rebase on top of 6.0.0-rc1
- Fix coding style issues
- Fix commit titles and commit messages
- Update dt-bindings :
- Add "allOf:if:then:"
- Use mt8192 as example (instead of mt8195)
- Fix dt-binding errors
- Fix DTS errors
v8:
- Fix coding style issues
- Rebase on top of next-20220803
- Add multi-instance support :
- Rewrite DT-binding and DTS :
- Add DT-binding and DTS for LVTS_v4 (MT8192 and MT8195)
- One LVTS node for each HW Domain (AP and MCU)
- One SW Instance for each HW Domain
- Add a Kconfig sub-menu entry for LVTS and LVTS_v4 SoCs
- Replace platform_get_resource by platform_get_mem_or_io to get
Base Address
- Replace platform_get_resource by platform_get_irq to get
Interrupt Number
- Add "lvts_" prefix to functions and structs
v7:
- Fix coding style issues
- Rewrite dt bindings
- was not accurate
- Use mt8195 for example (instead of mt8192)
- Rename mt6873 to mt8192
- Remove clock name
- Rebased on top of to series:
- patchwork.kernel.org/project/linux-mediatek/list/?series=637849
- patchwork.kernel.org/project/linux-pm/list/?series=639386
v6:
- Remove temperature aggregation (it will be added in another
series)
- Update the way to read the temperature (read one sensor
instead of all)
- Add support of mt8195
v5:
- Use 'git mv' for the relocated file.
v4:
- Rebase to kernel-v5.13-rc1
v3:
- change the expression in the lvts_temp_to_raw to dev_s64.
v2:
- Rebase to kernel-5.11-rc1.
- sort headers
- remove initial value 0 of msr_raw in the lvts_temp_to_raw.
- disconstruct the api of lvts_read_tc_msr_raw.
- add the initial value max_temp = 0 and compare e.q.
in the lvts_read_all_tc_temperature.
- add the return with an invalid number in the lvts_init.
Balsam CHIHI (6):
thermal/drivers/mediatek: Relocate driver to mediatek folder
dt-bindings/thermal/mediatek: Add LVTS thermal controllers dt-binding
definition
arm64/dts/mt8195: Add efuse node to mt8195
thermal/drivers/mediatek: Add the Low Voltage Thermal Sensor driver
arm64/dts/mt8195: Add thermal zones and thermal nodes
arm64/dts/mt8195: Add temperature mitigation threshold
.../thermal/mediatek,lvts-thermal.yaml | 107 ++
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 272 ++++
drivers/thermal/Kconfig | 14 +-
drivers/thermal/Makefile | 2 +-
drivers/thermal/mediatek/Kconfig | 36 +
drivers/thermal/mediatek/Makefile | 2 +
.../auxadc_thermal.c} | 2 +-
drivers/thermal/mediatek/lvts_thermal.c | 1261 +++++++++++++++++
include/dt-bindings/thermal/mediatek-lvts.h | 19 +
9 files changed, 1703 insertions(+), 12 deletions(-)
create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
create mode 100644 drivers/thermal/mediatek/Kconfig
create mode 100644 drivers/thermal/mediatek/Makefile
rename drivers/thermal/{mtk_thermal.c => mediatek/auxadc_thermal.c} (99%)
create mode 100644 drivers/thermal/mediatek/lvts_thermal.c
create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
base-commit: 0d568e144ead70189e7f16066dcb155b78ff9266
--
2.34.1
From: Balsam CHIHI <[email protected]>
Add MediaTek proprietary folder to upstream more thermal zone and cooler
drivers, relocate the original thermal controller driver to it, and rename it
as "auxadc_thermal.c" to show its purpose more clearly.
Signed-off-by: Balsam CHIHI <[email protected]>
---
drivers/thermal/Kconfig | 14 ++++---------
drivers/thermal/Makefile | 2 +-
drivers/thermal/mediatek/Kconfig | 21 +++++++++++++++++++
drivers/thermal/mediatek/Makefile | 1 +
.../auxadc_thermal.c} | 2 +-
5 files changed, 28 insertions(+), 12 deletions(-)
create mode 100644 drivers/thermal/mediatek/Kconfig
create mode 100644 drivers/thermal/mediatek/Makefile
rename drivers/thermal/{mtk_thermal.c => mediatek/auxadc_thermal.c} (99%)
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index eaeb2b2ee6e9..4cd7ab707315 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -416,16 +416,10 @@ config DA9062_THERMAL
zone.
Compatible with the DA9062 and DA9061 PMICs.
-config MTK_THERMAL
- tristate "Temperature sensor driver for mediatek SoCs"
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_IOMEM
- depends on NVMEM || NVMEM=n
- depends on RESET_CONTROLLER
- default y
- help
- Enable this option if you want to have support for thermal management
- controller present in Mediatek SoCs
+menu "Mediatek thermal drivers"
+depends on ARCH_MEDIATEK || COMPILE_TEST
+source "drivers/thermal/mediatek/Kconfig"
+endmenu
config AMLOGIC_THERMAL
tristate "Amlogic Thermal Support"
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 60f0dfa9aae2..1c460d189bd7 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -56,7 +56,7 @@ obj-y += st/
obj-y += qcom/
obj-y += tegra/
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
-obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
+obj-y += mediatek/
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
diff --git a/drivers/thermal/mediatek/Kconfig b/drivers/thermal/mediatek/Kconfig
new file mode 100644
index 000000000000..7558a847d4e9
--- /dev/null
+++ b/drivers/thermal/mediatek/Kconfig
@@ -0,0 +1,21 @@
+config MTK_THERMAL
+ tristate "MediaTek thermal drivers"
+ depends on THERMAL_OF
+ help
+ This is the option for MediaTek thermal software solutions.
+ Please enable corresponding options to get temperature
+ information from thermal sensors or turn on throttle
+ mechaisms for thermal mitigation.
+
+if MTK_THERMAL
+
+config MTK_SOC_THERMAL
+ tristate "AUXADC temperature sensor driver for MediaTek SoCs"
+ depends on HAS_IOMEM
+ help
+ Enable this option if you want to get SoC temperature
+ information for MediaTek platforms.
+ This driver configures thermal controllers to collect
+ temperature via AUXADC interface.
+
+endif
diff --git a/drivers/thermal/mediatek/Makefile b/drivers/thermal/mediatek/Makefile
new file mode 100644
index 000000000000..53e86e30b26f
--- /dev/null
+++ b/drivers/thermal/mediatek/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTK_SOC_THERMAL) += auxadc_thermal.o
diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mediatek/auxadc_thermal.c
similarity index 99%
rename from drivers/thermal/mtk_thermal.c
rename to drivers/thermal/mediatek/auxadc_thermal.c
index 9a8b107900e9..90ae6b5c530e 100644
--- a/drivers/thermal/mtk_thermal.c
+++ b/drivers/thermal/mediatek/auxadc_thermal.c
@@ -23,7 +23,7 @@
#include <linux/reset.h>
#include <linux/types.h>
-#include "thermal_hwmon.h"
+#include "../thermal_hwmon.h"
/* AUXADC Registers */
#define AUXADC_CON1_SET_V 0x008
--
2.34.1
From: Balsam CHIHI <[email protected]>
Add efuse node.
This will be required by the thermal driver to get the calibration data.
Signed-off-by: Balsam CHIHI <[email protected]>
Reviewed-by: Matthias Brugger <[email protected]>
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
---
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
index 5d31536f4c48..09df105f4606 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
@@ -1380,6 +1380,12 @@ pciephy_glb_intr: pciephy-glb-intr@193 {
dp_calibration: dp-data@1ac {
reg = <0x1ac 0x10>;
};
+ lvts_efuse_data1: lvts1-calib@1bc {
+ reg = <0x1bc 0x14>;
+ };
+ lvts_efuse_data2: lvts2-calib@1d0 {
+ reg = <0x1d0 0x38>;
+ };
};
u3phy2: t-phy@11c40000 {
--
2.34.1
From: Balsam CHIHI <[email protected]>
Add LVTS thermal controllers dt-binding definition for mt8195.
Signed-off-by: Balsam CHIHI <[email protected]>
---
.../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
include/dt-bindings/thermal/mediatek-lvts.h | 19 ++++
2 files changed, 126 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
new file mode 100644
index 000000000000..12bfbdd8ff89
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
+
+maintainers:
+ - Balsam CHIHI <[email protected]>
+
+description: |
+ LVTS is a thermal management architecture composed of three subsystems,
+ a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
+ a Converter - Low Voltage Thermal Sensor converter (LVTS), and
+ a Digital controller (LVTS_CTRL).
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8195-lvts-ap
+ - mediatek,mt8195-lvts-mcu
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+ description: LVTS reset for clearing temporary data on AP/MCU.
+
+ nvmem-cells:
+ minItems: 1
+ items:
+ - description: Calibration eFuse data 1 for LVTS
+ - description: Calibration eFuse data 2 for LVTS
+
+ nvmem-cell-names:
+ minItems: 1
+ items:
+ - const: lvts-calib-data-1
+ - const: lvts-calib-data-2
+
+ "#thermal-sensor-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - resets
+ - nvmem-cells
+ - nvmem-cell-names
+ - "#thermal-sensor-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8195-clk.h>
+ #include <dt-bindings/reset/mt8195-resets.h>
+ #include <dt-bindings/thermal/mediatek-lvts.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ lvts_mcu: thermal-sensor@11278000 {
+ compatible = "mediatek,mt8195-lvts-mcu";
+ reg = <0 0x11278000 0 0x1000>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+ };
+
+ thermal_zones: thermal-zones {
+ cpu0-thermal {
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
+
+ trips {
+ cpu0_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu0_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
new file mode 100644
index 000000000000..428a95c18509
--- /dev/null
+++ b/include/dt-bindings/thermal/mediatek-lvts.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Balsam CHIHI <[email protected]>
+ */
+
+#ifndef __MEDIATEK_LVTS_DT_H
+#define __MEDIATEK_LVTS_DT_H
+
+#define MT8195_MCU_BIG_CPU0 0
+#define MT8195_MCU_BIG_CPU1 1
+#define MT8195_MCU_BIG_CPU2 2
+#define MT8195_MCU_BIG_CPU3 3
+#define MT8195_MCU_LITTLE_CPU0 4
+#define MT8195_MCU_LITTLE_CPU1 5
+#define MT8195_MCU_LITTLE_CPU2 6
+#define MT8195_MCU_LITTLE_CPU3 7
+
+#endif /* __MEDIATEK_LVTS_DT_H */
--
2.34.1
From: Balsam CHIHI <[email protected]>
The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
controllers contained in a thermal domain.
A thermal domains can be the MCU or the AP.
Each thermal domains contain up to seven controllers, each thermal
controller handle up to four thermal sensors.
The LVTS has two Finite State Machines (FSM), one to handle the
functionin temperatures range like hot or cold temperature and another
one to handle monitoring trip point. The FSM notifies via interrupts
when a trip point is crossed.
The interrupt is managed at the thermal controller level, so when an
interrupt occurs, the driver has to find out which sensor triggered
such an interrupt.
The sampling of the thermal can be filtered or immediate. For the
former, the LVTS measures several points and applies a low pass
filter.
Signed-off-by: Balsam CHIHI <[email protected]>
---
drivers/thermal/mediatek/Kconfig | 15 +
drivers/thermal/mediatek/Makefile | 1 +
drivers/thermal/mediatek/lvts_thermal.c | 1261 +++++++++++++++++++++++
3 files changed, 1277 insertions(+)
create mode 100644 drivers/thermal/mediatek/lvts_thermal.c
diff --git a/drivers/thermal/mediatek/Kconfig b/drivers/thermal/mediatek/Kconfig
index 7558a847d4e9..99597d7b9890 100644
--- a/drivers/thermal/mediatek/Kconfig
+++ b/drivers/thermal/mediatek/Kconfig
@@ -18,4 +18,19 @@ config MTK_SOC_THERMAL
This driver configures thermal controllers to collect
temperature via AUXADC interface.
+config MTK_LVTS_THERMAL
+ tristate "LVTS Thermal Driver for MediaTek SoCs"
+ depends on HAS_IOMEM
+ help
+ Enable this option if you want to get SoC temperature
+ information for supported MediaTek platforms.
+ This driver configures LVTS (Low Voltage Thermal Sensor)
+ thermal controllers to collect temperatures via ASIF
+ (Analog Serial Interface).
+
+config MTK_LVTS_THERMAL_DEBUGFS
+ bool "LVTS thermal debugfs"
+ depends on MTK_LVTS_THERMAL && DEBUG_FS
+ help
+ Enable this option to debug the internals of the device driver.
endif
diff --git a/drivers/thermal/mediatek/Makefile b/drivers/thermal/mediatek/Makefile
index 53e86e30b26f..1c6daa1e644b 100644
--- a/drivers/thermal/mediatek/Makefile
+++ b/drivers/thermal/mediatek/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_MTK_SOC_THERMAL) += auxadc_thermal.o
+obj-$(CONFIG_MTK_LVTS_THERMAL) += lvts_thermal.o
diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c
new file mode 100644
index 000000000000..1e51af728aff
--- /dev/null
+++ b/drivers/thermal/mediatek/lvts_thermal.c
@@ -0,0 +1,1261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Balsam CHIHI <[email protected]>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/thermal.h>
+#include <dt-bindings/thermal/mediatek-lvts.h>
+
+#define LVTS_MONCTL0(__base) (__base + 0x0000)
+#define LVTS_MONCTL1(__base) (__base + 0x0004)
+#define LVTS_MONCTL2(__base) (__base + 0x0008)
+#define LVTS_MONINT(__base) (__base + 0x000C)
+#define LVTS_MONINTSTS(__base) (__base + 0x0010)
+#define LVTS_MONIDET0(__base) (__base + 0x0014)
+#define LVTS_MONIDET1(__base) (__base + 0x0018)
+#define LVTS_MONIDET2(__base) (__base + 0x001C)
+#define LVTS_MONIDET3(__base) (__base + 0x0020)
+#define LVTS_H2NTHRE(__base) (__base + 0x0024)
+#define LVTS_HTHRE(__base) (__base + 0x0028)
+#define LVTS_OFFSETH(__base) (__base + 0x0030)
+#define LVTS_OFFSETL(__base) (__base + 0x0034)
+#define LVTS_MSRCTL0(__base) (__base + 0x0038)
+#define LVTS_MSRCTL1(__base) (__base + 0x003C)
+#define LVTS_TSSEL(__base) (__base + 0x0040)
+#define LVTS_CALSCALE(__base) (__base + 0x0048)
+#define LVTS_ID(__base) (__base + 0x004C)
+#define LVTS_CONFIG(__base) (__base + 0x0050)
+#define LVTS_EDATA00(__base) (__base + 0x0054)
+#define LVTS_EDATA01(__base) (__base + 0x0058)
+#define LVTS_EDATA02(__base) (__base + 0x005C)
+#define LVTS_EDATA03(__base) (__base + 0x0060)
+#define LVTS_MSR0(__base) (__base + 0x0090)
+#define LVTS_MSR1(__base) (__base + 0x0094)
+#define LVTS_MSR2(__base) (__base + 0x0098)
+#define LVTS_MSR3(__base) (__base + 0x009C)
+#define LVTS_IMMD0(__base) (__base + 0x00A0)
+#define LVTS_IMMD1(__base) (__base + 0x00A4)
+#define LVTS_IMMD2(__base) (__base + 0x00A8)
+#define LVTS_IMMD3(__base) (__base + 0x00AC)
+#define LVTS_PROTCTL(__base) (__base + 0x00C0)
+#define LVTS_PROTTA(__base) (__base + 0x00C4)
+#define LVTS_PROTTB(__base) (__base + 0x00C8)
+#define LVTS_PROTTC(__base) (__base + 0x00CC)
+#define LVTS_CLKEN(__base) (__base + 0x00E4)
+
+#define LVTS_PERIOD_UNIT ((118 * 1000) / (256 * 38))
+#define LVTS_GROUP_INTERVAL 1
+#define LVTS_FILTER_INTERVAL 1
+#define LVTS_SENSOR_INTERVAL 1
+#define LVTS_HW_FILTER 0x2
+#define LVTS_TSSEL_CONF 0x13121110
+#define LVTS_CALSCALE_CONF 0x300
+#define LVTS_MONINT_CONF 0x9FBF7BDE
+
+#define LVTS_INT_SENSOR0 0x0009001F
+#define LVTS_INT_SENSOR1 0X000881F0
+#define LVTS_INT_SENSOR2 0x00247C00
+#define LVTS_INT_SENSOR3 0x1FC00000
+
+#define LVTS_SENSOR_MAX 4
+#define LVTS_GOLDEN_TEMP_MAX 62
+#define LVTS_GOLDEN_TEMP_DEFAULT 50
+#define LVTS_COEFF_A -250460
+#define LVTS_COEFF_B 250460
+
+#define LVTS_MSR_IMMEDIATE_MODE 0
+#define LVTS_MSR_FILTERED_MODE 1
+
+#define LVTS_HW_SHUTDOWN_MT8195 105000
+
+static int golden_temp = LVTS_GOLDEN_TEMP_DEFAULT;
+static int coeff_b = LVTS_COEFF_B;
+
+struct lvts_sensor_data {
+ int dt_id;
+};
+
+struct lvts_ctrl_data {
+ struct lvts_sensor_data lvts_sensor[LVTS_SENSOR_MAX];
+ int cal_offset[LVTS_SENSOR_MAX];
+ int hw_tshut_temp;
+ int num_lvts_sensor;
+ int offset;
+ int mode;
+};
+
+struct lvts_data {
+ struct lvts_ctrl_data *lvts_ctrl;
+ int num_lvts_ctrl;
+};
+
+struct lvts_sensor {
+ struct thermal_zone_device *tz;
+ void __iomem *msr;
+ void __iomem *base;
+ int id;
+ int dt_id;
+};
+
+struct lvts_ctrl {
+ struct lvts_sensor sensors[LVTS_SENSOR_MAX];
+ u32 calibration[LVTS_SENSOR_MAX];
+ u32 hw_tshut_raw_temp;
+ int num_lvts_sensor;
+ int mode;
+ void __iomem *base;
+};
+
+struct lvts_domain {
+ struct lvts_ctrl *lvts_ctrl;
+ struct reset_control *reset;
+ struct clk *clk;
+ int num_lvts_ctrl;
+ void __iomem *base;
+ size_t calib_len;
+ u8 *calib;
+};
+
+#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
+
+static struct dentry *root;
+
+#define LVTS_DEBUG_FS_REGS(__reg) \
+{ \
+ .name = __stringify(__reg), \
+ .offset = __reg(0), \
+}
+
+static const struct debugfs_reg32 lvts_regs[] = {
+ LVTS_DEBUG_FS_REGS(LVTS_MONCTL0),
+ LVTS_DEBUG_FS_REGS(LVTS_MONCTL1),
+ LVTS_DEBUG_FS_REGS(LVTS_MONCTL2),
+ LVTS_DEBUG_FS_REGS(LVTS_MONINT),
+ LVTS_DEBUG_FS_REGS(LVTS_MONINTSTS),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET0),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET1),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET2),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET3),
+ LVTS_DEBUG_FS_REGS(LVTS_H2NTHRE),
+ LVTS_DEBUG_FS_REGS(LVTS_HTHRE),
+ LVTS_DEBUG_FS_REGS(LVTS_OFFSETH),
+ LVTS_DEBUG_FS_REGS(LVTS_OFFSETL),
+ LVTS_DEBUG_FS_REGS(LVTS_MSRCTL0),
+ LVTS_DEBUG_FS_REGS(LVTS_MSRCTL1),
+ LVTS_DEBUG_FS_REGS(LVTS_TSSEL),
+ LVTS_DEBUG_FS_REGS(LVTS_CALSCALE),
+ LVTS_DEBUG_FS_REGS(LVTS_ID),
+ LVTS_DEBUG_FS_REGS(LVTS_CONFIG),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA00),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA01),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA02),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA03),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR0),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR1),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR2),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR3),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD0),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD1),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD2),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD3),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTCTL),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTTA),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTTB),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTTC),
+ LVTS_DEBUG_FS_REGS(LVTS_CLKEN),
+};
+
+static int lvts_debugfs_init(struct device *dev,
+ struct lvts_domain *lvts_td)
+{
+ struct debugfs_regset32 *regset;
+ struct lvts_ctrl *lvts_ctrl;
+ struct dentry *dentry;
+ struct dentry *dom_dentry;
+ char name[64];
+ int i;
+
+ if (!root)
+ root = debugfs_create_dir("lvts", NULL);
+
+ dom_dentry = debugfs_create_dir(dev_name(dev), root);
+ if (!dom_dentry)
+ return 0;
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+
+ lvts_ctrl = &lvts_td->lvts_ctrl[i];
+
+ sprintf(name, "controller%d", i);
+ dentry = debugfs_create_dir(name, dom_dentry);
+ if (!dentry)
+ continue;
+
+ regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL);
+ if (!regset)
+ continue;
+
+ regset->base = lvts_ctrl->base;
+ regset->regs = lvts_regs;
+ regset->nregs = ARRAY_SIZE(lvts_regs);
+
+ debugfs_create_regset32("registers", 0400, dentry, regset);
+ }
+
+ return 0;
+}
+
+static void lvts_debugfs_exit(void)
+{
+ debugfs_remove_recursive(root);
+}
+
+#else
+
+static inline int lvts_debugfs_init(struct device *dev,
+ struct lvts_domain *lvts_td)
+{
+ return 0;
+}
+
+static void lvts_debugfs_exit(void) { }
+
+#endif
+
+static int lvts_raw_to_temp(u32 raw_temp)
+{
+ int temperature;
+
+ temperature = ((s64)(raw_temp & 0xFFFF) * LVTS_COEFF_A) >> 14;
+ temperature += coeff_b;
+
+ return temperature;
+}
+
+static u32 lvts_temp_to_raw(int temperature)
+{
+ u32 raw_temp = ((s64)(coeff_b - temperature)) << 14;
+
+ raw_temp = div_s64(raw_temp, -LVTS_COEFF_A);
+
+ return raw_temp;
+}
+
+static int lvts_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct lvts_sensor *lvts_sensor = tz->devdata;
+ void __iomem *msr = lvts_sensor->msr;
+ u32 value;
+
+ /*
+ * Measurement registers:
+ *
+ * LVTS_MSR[0-3] / LVTS_IMMD[0-3]
+ *
+ * Bits:
+ *
+ * 32-17: Unused
+ * 16 : Valid temperature
+ * 15-0 : Raw temperature
+ */
+ value = readl(msr);
+
+ /*
+ * As the thermal zone temperature will read before the
+ * hardware sensor is fully initialized, we have to check the
+ * validity of the temperature returned when reading the
+ * measurement register. The thermal controller will set the
+ * valid bit temperature only when it is totally initialized.
+ *
+ * Otherwise, we may end up with garbage values out of the
+ * functionning temperature and directly jump to a system
+ * shutdown.
+ */
+ if (!(value & BIT(16)))
+ return -EAGAIN;
+
+ *temp = lvts_raw_to_temp(value & 0xFFFF);
+
+ return 0;
+}
+
+static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high)
+{
+ struct lvts_sensor *lvts_sensor = tz->devdata;
+ void __iomem *base = lvts_sensor->base;
+ u32 raw_low = lvts_temp_to_raw(low);
+ u32 raw_high = lvts_temp_to_raw(high);
+
+ /*
+ * Hot to normal temperature threshold
+ *
+ * LVTS_H2NTHRE
+ *
+ * Bits:
+ *
+ * 14-0 : Raw temperature for threshold
+ */
+ if (low != -INT_MAX) {
+ dev_dbg(&tz->device, "Setting low limit temperature interrupt: %d\n", low);
+ writel(raw_low, LVTS_H2NTHRE(base));
+ }
+
+ /*
+ * Hot temperature threshold
+ *
+ * LVTS_HTHRE
+ *
+ * Bits:
+ *
+ * 14-0 : Raw temperature for threshold
+ */
+ dev_dbg(&tz->device, "Setting high limit temperature interrupt: %d\n", high);
+ writel(raw_high, LVTS_HTHRE(base));
+
+ return 0;
+}
+
+static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl)
+{
+ irqreturn_t iret = IRQ_NONE;
+ u32 value, masks[] = {
+ LVTS_INT_SENSOR0,
+ LVTS_INT_SENSOR1,
+ LVTS_INT_SENSOR2,
+ LVTS_INT_SENSOR3
+ };
+ int i;
+
+ /*
+ * Interrupt monitoring status
+ *
+ * LVTS_MONINTST
+ *
+ * Bits:
+ *
+ * 31 : Interrupt for stage 3
+ * 30 : Interrupt for stage 2
+ * 29 : Interrupt for state 1
+ * 28 : Interrupt using filter on sensor 3
+ *
+ * 27 : Interrupt using immediate on sensor 3
+ * 26 : Interrupt normal to hot on sensor 3
+ * 25 : Interrupt high offset on sensor 3
+ * 24 : Interrupt low offset on sensor 3
+ *
+ * 23 : Interrupt hot threshold on sensor 3
+ * 22 : Interrupt cold threshold on sensor 3
+ * 21 : Interrupt using filter on sensor 2
+ * 20 : Interrupt using filter on sensor 1
+ *
+ * 19 : Interrupt using filter on sensor 0
+ * 18 : Interrupt using immediate on sensor 2
+ * 17 : Interrupt using immediate on sensor 1
+ * 16 : Interrupt using immediate on sensor 0
+ *
+ * 15 : Interrupt device access timeout interrupt
+ * 14 : Interrupt normal to hot on sensor 2
+ * 13 : Interrupt high offset interrupt on sensor 2
+ * 12 : Interrupt low offset interrupt on sensor 2
+ *
+ * 11 : Interrupt hot threshold on sensor 2
+ * 10 : Interrupt cold threshold on sensor 2
+ * 9 : Interrupt normal to hot on sensor 1
+ * 8 : Interrupt high offset interrupt on sensor 1
+ *
+ * 7 : Interrupt low offset interrupt on sensor 1
+ * 6 : Interrupt hot threshold on sensor 1
+ * 5 : Interrupt cold threshold on sensor 1
+ * 4 : Interrupt normal to hot on sensor 0
+ *
+ * 3 : Interrupt high offset interrupt on sensor 0
+ * 2 : Interrupt low offset interrupt on sensor 0
+ * 1 : Interrupt hot threshold on sensor 0
+ * 0 : Interrupt cold threshold on sensor 0
+ *
+ * We are interested in the sensor(s) responsible of the
+ * interrupt event. We update the thermal framework with the
+ * thermal zone associated with the sensor. The framework will
+ * take care of the rest whatever the kind of interrupt, we
+ * are only interested in which sensor raised the interrupt.
+ *
+ * sensor 3 interrupt: 0001 1111 1100 0000 0000 0000 0000 0000
+ * => 0x1FC00000
+ * sensor 2 interrupt: 0000 0000 0010 0100 0111 1100 0000 0000
+ * => 0x00247C00
+ * sensor 1 interrupt: 0000 0000 0001 0001 0000 0011 1110 0000
+ * => 0X000881F0
+ * sensor 0 interrupt: 0000 0000 0000 1001 0000 0000 0001 1111
+ * => 0x0009001F
+ */
+ value = readl(LVTS_MONINTSTS(lvts_ctrl->base));
+
+ /*
+ * Let's figure out which sensors raised the interrupt
+ *
+ * NOTE: the masks array must be ordered with the index
+ * corresponding to the sensor id eg. index=0, mask for
+ * sensor0.
+ */
+ for (i = 0; i < ARRAY_SIZE(masks); i++) {
+
+ if (!(value & masks[i]))
+ continue;
+
+ thermal_zone_device_update(lvts_ctrl->sensors[i].tz,
+ THERMAL_TRIP_VIOLATED);
+ iret = IRQ_HANDLED;
+ }
+
+ /*
+ * Write back to clear the interrupt status (W1C)
+ */
+ writel(value, LVTS_MONINTSTS(lvts_ctrl->base));
+
+ return iret;
+}
+
+/*
+ * Temperature interrupt handler. Even if the driver supports more
+ * interrupt modes, we use the interrupt when the temperature crosses
+ * the hot threshold the way up and the way down (modulo the
+ * hysteresis).
+ *
+ * Each thermal domain has a couple of interrupts, one for hardware
+ * reset and another one for all the thermal events happening on the
+ * different sensors.
+ *
+ * The interrupt is configured for thermal events when crossing the
+ * hot temperature limit. At each interrupt, we check in every
+ * controller if there is an interrupt pending.
+ */
+static irqreturn_t lvts_irq_handler(int irq, void *data)
+{
+ struct lvts_domain *lvts_td = data;
+ irqreturn_t aux, iret = IRQ_NONE;
+ int i;
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+
+ aux = lvts_ctrl_irq_handler(lvts_td->lvts_ctrl);
+ if (aux != IRQ_HANDLED)
+ continue;
+
+ iret = IRQ_HANDLED;
+ }
+
+ return iret;
+}
+
+static struct thermal_zone_device_ops lvts_ops = {
+ .get_temp = lvts_get_temp,
+ .set_trips = lvts_set_trips,
+};
+
+static int __init lvts_sensor_init(struct device *dev,
+ struct lvts_ctrl *lvts_ctrl,
+ struct lvts_ctrl_data *lvts_ctrl_data)
+{
+ struct lvts_sensor *lvts_sensor = lvts_ctrl->sensors;
+ void __iomem *msr_regs[] = {
+ LVTS_MSR0(lvts_ctrl->base),
+ LVTS_MSR1(lvts_ctrl->base),
+ LVTS_MSR2(lvts_ctrl->base),
+ LVTS_MSR3(lvts_ctrl->base)
+ };
+
+ void __iomem *imm_regs[] = {
+ LVTS_IMMD0(lvts_ctrl->base),
+ LVTS_IMMD1(lvts_ctrl->base),
+ LVTS_IMMD2(lvts_ctrl->base),
+ LVTS_IMMD3(lvts_ctrl->base)
+ };
+
+ int i;
+
+ for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) {
+
+ int dt_id = lvts_ctrl_data->lvts_sensor[i].dt_id;
+
+ /*
+ * At this point, we don't know which id matches which
+ * sensor. Let's set arbitrally the id from the index.
+ */
+ lvts_sensor[i].id = i;
+
+ /*
+ * The thermal zone registration will set the trip
+ * point interrupt in the thermal controller
+ * register. But this one will be reset in the
+ * initialization after. So we need to post pone the
+ * thermal zone creation after the controller is
+ * setup. For this reason, we store the device tree
+ * node id from the data in the sensor structure
+ */
+ lvts_sensor[i].dt_id = dt_id;
+
+ /*
+ * We assign the base address of the thermal
+ * controller as a back pointer. So it will be
+ * accessible from the different thermal framework ops
+ * as we pass the lvts_sensor pointer as thermal zone
+ * private data.
+ */
+ lvts_sensor[i].base = lvts_ctrl->base;
+
+ /*
+ * Each sensor has its own register address to read from.
+ */
+ lvts_sensor[i].msr = lvts_ctrl_data->mode == LVTS_MSR_IMMEDIATE_MODE ?
+ imm_regs[i] : msr_regs[i];
+ };
+
+ lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor;
+
+ return 0;
+}
+
+/*
+ * The efuse blob values follows the sensor enumeration per thermal
+ * controller. The decoding of the stream is as follow:
+ *
+ * <--?-> <----big0 ???---> <-sensor0-> <-0->
+ * ------------------------------------------
+ * index in the stream: : | 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 |
+ * ------------------------------------------
+ *
+ * <--sensor1--><-0-> <----big1 ???---> <-sen
+ * ------------------------------------------
+ * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
+ * ------------------------------------------
+ *
+ * sor0-> <-0-> <-sensor1-> <-0-> ..........
+ * ------------------------------------------
+ * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
+ * ------------------------------------------
+ *
+ * And so on ...
+ *
+ * The data description gives the offset of the calibration data in
+ * this bytes stream for each sensor.
+ *
+ * Each thermal controller can handle up to 4 sensors max, we don't
+ * care if there are less as the array of calibration is sized to 4
+ * anyway. The unused sensor slot will be zeroed.
+ */
+static int __init lvts_calibration_init(struct device *dev,
+ struct lvts_ctrl *lvts_ctrl,
+ struct lvts_ctrl_data *lvts_ctrl_data,
+ u8 *efuse_calibration)
+{
+ int i;
+
+ for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++)
+ memcpy(&lvts_ctrl->calibration[i],
+ efuse_calibration + lvts_ctrl_data->cal_offset[i], 2);
+
+ return 0;
+}
+
+/*
+ * The efuse bytes stream can be split into different chunk of
+ * nvmems. This function reads and concatenate those into a single
+ * buffer so it can be read sequentially when initializing the
+ * calibration data.
+ */
+static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td,
+ struct lvts_data *lvts_data)
+{
+ struct device_node *np = dev_of_node(dev);
+ struct nvmem_cell *cell;
+ struct property *prop;
+ const char *cell_name;
+
+ of_property_for_each_string(np, "nvmem-cell-names", prop, cell_name) {
+ size_t len;
+ u8 *efuse;
+
+ cell = of_nvmem_cell_get(np, cell_name);
+ if (IS_ERR(cell)) {
+ dev_dbg(dev, "Failed to get cell '%s'\n", cell_name);
+ return PTR_ERR(cell);
+ }
+
+ efuse = nvmem_cell_read(cell, &len);
+
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(efuse)) {
+ dev_dbg(dev, "Failed to read cell '%s'\n", cell_name);
+ return PTR_ERR(efuse);
+ }
+
+ lvts_td->calib = devm_krealloc(dev, lvts_td->calib,
+ lvts_td->calib_len + len, GFP_KERNEL);
+ if (!lvts_td->calib)
+ return -ENOMEM;
+
+ memcpy(lvts_td->calib + lvts_td->calib_len, efuse, len);
+
+ lvts_td->calib_len += len;
+
+ kfree(efuse);
+ }
+
+ return 0;
+}
+
+static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
+{
+ u32 gt;
+
+ gt = (*value) >> 24;
+
+ if (gt && gt < LVTS_GOLDEN_TEMP_MAX)
+ golden_temp = gt;
+
+ coeff_b = golden_temp * 500 + LVTS_COEFF_B;
+
+ return 0;
+}
+
+static int __init lvts_ctrl_init(struct device *dev,
+ struct lvts_domain *lvts_td,
+ struct lvts_data *lvts_data)
+{
+ size_t size = sizeof(*lvts_td->lvts_ctrl) * lvts_data->num_lvts_ctrl;
+ struct lvts_ctrl *lvts_ctrl;
+ int i, ret;
+
+ /*
+ * Create the calibration bytes stream from efuse data
+ */
+ ret = lvts_calibration_read(dev, lvts_td, lvts_data);
+ if (ret)
+ return ret;
+
+ /*
+ * The golden temp information is contained in the first chunk
+ * of efuse data.
+ */
+ ret = lvts_golden_temp_init(dev, (u32 *)lvts_td->calib);
+ if (ret)
+ return ret;
+
+ lvts_ctrl = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!lvts_ctrl)
+ return -ENOMEM;
+
+ for (i = 0; i < lvts_data->num_lvts_ctrl; i++) {
+
+ lvts_ctrl[i].base = lvts_td->base + lvts_data->lvts_ctrl[i].offset;
+
+ ret = lvts_sensor_init(dev, &lvts_ctrl[i],
+ &lvts_data->lvts_ctrl[i]);
+ if (ret)
+ return ret;
+
+ ret = lvts_calibration_init(dev, &lvts_ctrl[i],
+ &lvts_data->lvts_ctrl[i],
+ lvts_td->calib);
+ if (ret)
+ return ret;
+
+ /*
+ * The mode the ctrl will use to read the temperature
+ * (filtered or immediate)
+ */
+ lvts_ctrl[i].mode = lvts_data->lvts_ctrl[i].mode;
+
+ /*
+ * The temperature to raw temperature must be done
+ * after initializing the calibration.
+ */
+ lvts_ctrl[i].hw_tshut_raw_temp =
+ lvts_temp_to_raw(lvts_data->lvts_ctrl[i].hw_tshut_temp);
+ }
+
+ /*
+ * We no longer need the efuse bytes stream, let's free it
+ */
+ devm_kfree(dev, lvts_td->calib);
+
+ lvts_td->lvts_ctrl = lvts_ctrl;
+ lvts_td->num_lvts_ctrl = lvts_data->num_lvts_ctrl;
+
+ return 0;
+}
+
+/*
+ * At this point the configuration register is the only place in the
+ * driver where we write multiple values. Per hardware constraint,
+ * each write in the configuration register must be separated by a
+ * delay of 2 us.
+ */
+static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, u32 *cmds, int nr_cmds)
+{
+ int i;
+
+ /*
+ * Configuration register
+ */
+ for (i = 0; i < nr_cmds; i++) {
+ writel(cmds[i], LVTS_CONFIG(lvts_ctrl->base));
+ usleep_range(2, 4);
+ }
+}
+
+static int lvts_irq_init(struct lvts_ctrl *lvts_ctrl)
+{
+ /*
+ * LVTS_PROTCTL : Thermal Protection Sensor Selection
+ *
+ * Bits:
+ *
+ * 19-18 : Sensor to base the protection on
+ * 17-16 : Strategy:
+ * 00 : Average of 4 sensors
+ * 01 : Max of 4 sensors
+ * 10 : Selected sensor with bits 19-18
+ * 11 : Reserved
+ */
+ writel(BIT(16), LVTS_PROTCTL(lvts_ctrl->base));
+
+ /*
+ * LVTS_PROTTA : Stage 1 temperature threshold
+ * LVTS_PROTTB : Stage 2 temperature threshold
+ * LVTS_PROTTC : Stage 3 temperature threshold
+ *
+ * Bits:
+ *
+ * 14-0: Raw temperature threshold
+ *
+ * writel(0x0, LVTS_PROTTA(lvts_ctrl->base));
+ * writel(0x0, LVTS_PROTTB(lvts_ctrl->base));
+ */
+ writel(lvts_ctrl->hw_tshut_raw_temp, LVTS_PROTTC(lvts_ctrl->base));
+
+ /*
+ * LVTS_MONINT : Interrupt configuration register
+ *
+ * The LVTS_MONINT register layout is the same as the LVTS_MONINTSTS
+ * register, except we set the bits to enable the interrupt.
+ */
+ writel(LVTS_MONINT_CONF, LVTS_MONINT(lvts_ctrl->base));
+
+ return 0;
+}
+
+static int lvts_domain_reset(struct device *dev, struct reset_control *reset)
+{
+ int ret;
+
+ ret = reset_control_assert(reset);
+ if (ret)
+ return ret;
+
+ return reset_control_deassert(reset);
+}
+
+/*
+ * Enable or disable the clocks of a specified thermal controller
+ */
+static int lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable)
+{
+ /*
+ * LVTS_CLKEN : Internal LVTS clock
+ *
+ * Bits:
+ *
+ * 0 : enable / disable clock
+ */
+ writel(enable, LVTS_CLKEN(lvts_ctrl->base));
+
+ return 0;
+}
+
+static inline int lvts_ctrl_enable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ return lvts_ctrl_set_enable(lvts_ctrl, 1);
+}
+
+static inline int lvts_ctrl_disable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ return lvts_ctrl_set_enable(lvts_ctrl, 0);
+}
+
+static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 };
+
+ lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
+
+ /*
+ * LVTS_ID : Get ID and status of the thermal controller
+ *
+ * Bits:
+ *
+ * 0-5 : thermal controller id
+ * 7 : thermal controller connection is valid
+ */
+ id = readl(LVTS_ID(lvts_ctrl->base));
+ if (!(id & BIT(7)))
+ return -EIO;
+
+ return 0;
+}
+
+static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ /*
+ * Write device mask: 0xC1030000
+ */
+ u32 cmds[] = {
+ 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
+ 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
+ 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
+ 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
+ };
+
+ lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
+
+ return 0;
+}
+
+static int lvts_ctrl_calibrate(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ int i;
+ void __iomem *lvts_edata[] = {
+ LVTS_EDATA00(lvts_ctrl->base),
+ LVTS_EDATA01(lvts_ctrl->base),
+ LVTS_EDATA02(lvts_ctrl->base),
+ LVTS_EDATA03(lvts_ctrl->base)
+ };
+
+ /*
+ * LVTS_EDATA0X : Efuse calibration reference value for sensor X
+ *
+ * Bits:
+ *
+ * 20-0 : Efuse value for normalization data
+ */
+ for (i = 0; i < LVTS_SENSOR_MAX; i++)
+ writel(lvts_ctrl->calibration[i], lvts_edata[i]);
+
+ return 0;
+}
+
+static int lvts_ctrl_configure(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ u32 value;
+
+ /*
+ * LVTS_TSSEL : Sensing point index numbering
+ *
+ * Bits:
+ *
+ * 31-24: ADC Sense 3
+ * 23-16: ADC Sense 2
+ * 15-8 : ADC Sense 1
+ * 7-0 : ADC Sense 0
+ */
+ value = LVTS_TSSEL_CONF;
+ writel(value, LVTS_TSSEL(lvts_ctrl->base));
+
+ /*
+ * LVTS_CALSCALE : ADC voltage round
+ */
+ value = 0x300;
+ value = LVTS_CALSCALE_CONF;
+
+ /*
+ * LVTS_MSRCTL0 : Sensor filtering strategy
+ *
+ * Filters:
+ *
+ * 000 : One sample
+ * 001 : Avg 2 samples
+ * 010 : 4 samples, drop min and max, avg 2 samples
+ * 011 : 6 samples, drop min and max, avg 4 samples
+ * 100 : 10 samples, drop min and max, avg 8 samples
+ * 101 : 18 samples, drop min and max, avg 16 samples
+ *
+ * Bits:
+ *
+ * 0-2 : Sensor0 filter
+ * 3-5 : Sensor1 filter
+ * 6-8 : Sensor2 filter
+ * 9-11 : Sensor3 filter
+ */
+ value = LVTS_HW_FILTER << 9 | LVTS_HW_FILTER << 6 |
+ LVTS_HW_FILTER << 3 | LVTS_HW_FILTER;
+ writel(value, LVTS_MSRCTL0(lvts_ctrl->base));
+
+ /*
+ * LVTS_MSRCTL1 : Measurement control
+ *
+ * Bits:
+ *
+ * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3
+ * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2
+ * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1
+ * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0
+ *
+ * That configuration will ignore the filtering and the delays
+ * introduced below in MONCTL1 and MONCTL2
+ */
+ if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) {
+ value = BIT(9) | BIT(6) | BIT(5) | BIT(4);
+ writel(value, LVTS_MSRCTL1(lvts_ctrl->base));
+ }
+
+ /*
+ * LVTS_MONCTL1 : Period unit and group interval configuration
+ *
+ * The clock source of LVTS thermal controller is 26MHz.
+ *
+ * The period unit is a time base for all the interval delays
+ * specified in the registers. By default we use 12. The time
+ * conversion is done by multiplying by 256 and 1/26.10^6
+ *
+ * An interval delay multiplied by the period unit gives the
+ * duration in seconds.
+ *
+ * - Filter interval delay is a delay between two samples of
+ * the same sensor.
+ *
+ * - Sensor interval delay is a delay between two samples of
+ * different sensors.
+ *
+ * - Group interval delay is a delay between different rounds.
+ *
+ * For example:
+ * If Period unit = C, filter delay = 1, sensor delay = 2, group delay = 1,
+ * and two sensors, TS1 and TS2, are in a LVTS thermal controller
+ * and then
+ * Period unit time = C * 1/26M * 256 = 12 * 38.46ns * 256 = 118.149us
+ * Filter interval delay = 1 * Period unit = 118.149us
+ * Sensor interval delay = 2 * Period unit = 236.298us
+ * Group interval delay = 1 * Period unit = 118.149us
+ *
+ * TS1 TS1 ... TS1 TS2 TS2 ... TS2 TS1...
+ * <--> Filter interval delay
+ * <--> Sensor interval delay
+ * <--> Group interval delay
+ * Bits:
+ * 29 - 20 : Group interval
+ * 16 - 13 : Send a single interrupt when crossing the hot threshold (1)
+ * or an interrupt everytime the hot threshold is crossed (0)
+ * 9 - 0 : Period unit
+ *
+ */
+ value = LVTS_GROUP_INTERVAL << 20 | LVTS_PERIOD_UNIT;
+ writel(value, LVTS_MONCTL1(lvts_ctrl->base));
+
+ /*
+ * LVTS_MONCTL2 : Filtering and sensor interval
+ *
+ * Bits:
+ *
+ * 25-16 : Interval unit in PERIOD_UNIT between sample on
+ * the same sensor, filter interval
+ * 9-0 : Interval unit in PERIOD_UNIT between each sensor
+ *
+ */
+ value = LVTS_FILTER_INTERVAL << 16 | LVTS_SENSOR_INTERVAL;
+ writel(value, LVTS_MONCTL2(lvts_ctrl->base));
+
+ return lvts_irq_init(lvts_ctrl);
+}
+
+static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ struct lvts_sensor *lvts_sensors = lvts_ctrl->sensors;
+ struct thermal_zone_device *tz;
+ u32 sensor_map = 0;
+ int i;
+
+ for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) {
+
+ int dt_id = lvts_sensors[i].dt_id;
+
+ tz = devm_thermal_of_zone_register(dev, dt_id, &lvts_sensors[i],
+ &lvts_ops);
+ if (IS_ERR(tz)) {
+ /*
+ * This thermal zone is not described in the
+ * device tree. It is not an error from the
+ * thermal OF code POV, we just continue.
+ */
+ if (PTR_ERR(tz) == -ENODEV)
+ continue;
+
+ return PTR_ERR(tz);
+ }
+
+ /*
+ * The thermal zone pointer will be needed in the
+ * interrupt handler, we store it in the sensor
+ * structure. The thermal domain structure will be
+ * passed to the interrupt handler private data as the
+ * interrupt is shared for all the controller
+ * belonging to the thermal domain.
+ */
+ lvts_sensors[i].tz = tz;
+
+ /*
+ * This sensor was correctly associated with a thermal
+ * zone, let's set the corresponding bit in the sensor
+ * map, so we can enable the temperature monitoring in
+ * the hardware thermal controller.
+ */
+ sensor_map |= BIT(i);
+ }
+
+ /*
+ * Bits:
+ * 9: Single point access flow
+ * 0-3: Enable sensing point 0-3
+ *
+ * The initialization of the thermal zones give us
+ * which sensor point to enable. If any thermal zone
+ * was not described in the device tree, it won't be
+ * enabled here in the sensor map.
+ */
+ writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
+
+ return 0;
+}
+
+static int lvts_domain_init(struct device *dev, struct lvts_domain *lvts_td,
+ struct lvts_data *lvts_data)
+{
+ struct lvts_ctrl *lvts_ctrl;
+ int i, ret;
+
+ ret = lvts_ctrl_init(dev, lvts_td, lvts_data);
+ if (ret)
+ return ret;
+
+ ret = lvts_domain_reset(dev, lvts_td->reset);
+ if (ret) {
+ dev_dbg(dev, "Failed to reset domain");
+ return ret;
+ }
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+
+ lvts_ctrl = &lvts_td->lvts_ctrl[i];
+
+ /*
+ * Initialization steps:
+ *
+ * - Enable the clock
+ * - Connect to the LVTS
+ * - Initialize the LVTS
+ * - Prepare the calibration data
+ * - Select monitored sensors
+ * [ Configure sampling ]
+ * [ Configure the interrupt ]
+ * - Start measurement
+ */
+ ret = lvts_ctrl_enable(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to enable LVTS clock");
+ return ret;
+ }
+
+ ret = lvts_ctrl_connect(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to connect to LVTS controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_initialize(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to initialize controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_calibrate(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to calibrate controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_configure(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to configure controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_start(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to start controller");
+ return ret;
+ }
+ }
+
+ return lvts_debugfs_init(dev, lvts_td);
+}
+
+static int lvts_probe(struct platform_device *pdev)
+{
+ struct lvts_data *lvts_data;
+ struct lvts_domain *lvts_td;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int irq, ret;
+
+ lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL);
+ if (!lvts_td)
+ return -ENOMEM;
+
+ lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
+ if (!lvts_data) {
+ dev_dbg(dev, "No platforme data");
+ return -ENODATA;
+ };
+
+ lvts_td->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(lvts_td->clk)) {
+ dev_dbg(dev, "Failed to retrieve clock\n");
+ return PTR_ERR(lvts_td->clk);
+ }
+
+ res = platform_get_mem_or_io(pdev, 0);
+ if (!res) {
+ dev_dbg(dev, "No IO resource\n");
+ return -ENXIO;
+ }
+
+ lvts_td->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lvts_td->base)) {
+ dev_dbg(dev, "Failed to map io resource\n");
+ return PTR_ERR(lvts_td->base);
+ }
+
+ lvts_td->reset = devm_reset_control_get_by_index(dev, 0);
+ if (IS_ERR(lvts_td->reset)) {
+ dev_dbg(dev, "Failed to get reset control\n");
+ return PTR_ERR(lvts_td->reset);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_dbg(dev, "No irq resource\n");
+ return irq;
+ }
+
+ ret = lvts_domain_init(dev, lvts_td, lvts_data);
+ if (ret) {
+ dev_dbg(dev, "Failed to initialize the lvts domain\n");
+ return ret;
+ }
+
+ /*
+ * At this point the LVTS is initialized and enabled. We can
+ * safely enable the interrupt.
+ */
+ ret = devm_request_threaded_irq(dev, irq, NULL, lvts_irq_handler,
+ IRQF_ONESHOT, dev_name(dev), lvts_td);
+ if (ret) {
+ dev_dbg(dev, "Failed to request interrupt\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lvts_td);
+
+ return 0;
+}
+
+static int lvts_remove(struct platform_device *pdev)
+{
+ struct lvts_domain *lvts_td;
+ struct device *dev = &pdev->dev;
+ int i;
+
+ lvts_td = platform_get_drvdata(pdev);
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
+ lvts_ctrl_disable(dev, &lvts_td->lvts_ctrl[i]);
+
+ lvts_debugfs_exit();
+
+ return 0;
+}
+
+static struct lvts_ctrl_data mt8195_lvts_data_ctrl[] = {
+ {
+ .cal_offset = { 0x4, 0x7 },
+ .lvts_sensor = {
+ { .dt_id = MT8195_MCU_BIG_CPU0 },
+ { .dt_id = MT8195_MCU_BIG_CPU1 }
+ },
+ .num_lvts_sensor = 2,
+ .offset = 0x0,
+ .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
+ },
+
+ {
+ .cal_offset = { 0xd, 0x10 },
+ .lvts_sensor = {
+ { .dt_id = MT8195_MCU_BIG_CPU2 },
+ { .dt_id = MT8195_MCU_BIG_CPU3 }
+ },
+ .num_lvts_sensor = 2,
+ .offset = 0x100,
+ .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
+ },
+
+ {
+ .cal_offset = { 0x16, 0x19, 0x1c, 0x1f },
+ .lvts_sensor = {
+ { .dt_id = MT8195_MCU_LITTLE_CPU0 },
+ { .dt_id = MT8195_MCU_LITTLE_CPU1 },
+ { .dt_id = MT8195_MCU_LITTLE_CPU2 },
+ { .dt_id = MT8195_MCU_LITTLE_CPU3 }
+ },
+ .num_lvts_sensor = 4,
+ .offset = 0x200,
+ .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
+ }
+};
+
+static struct lvts_data mt8195_lvts_mcu_data = {
+ .lvts_ctrl = mt8195_lvts_data_ctrl,
+ .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_data_ctrl),
+};
+
+static const struct of_device_id lvts_of_match[] = {
+ { .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lvts_of_match);
+
+static struct platform_driver lvts_driver = {
+ .probe = lvts_probe,
+ .remove = lvts_remove,
+ .driver = {
+ .name = "mtk-lvts-thermal",
+ .of_match_table = lvts_of_match,
+ },
+};
+module_platform_driver(lvts_driver);
+
+MODULE_AUTHOR("Balsam CHIHI <[email protected]>");
+MODULE_DESCRIPTION("MediaTek LVTS Thermal Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
From: Balsam CHIHI <[email protected]>
Add thermal zones and thermal nodes for the mt8195.
Signed-off-by: Balsam CHIHI <[email protected]>
---
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 129 +++++++++++++++++++++++
1 file changed, 129 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
index 09df105f4606..636676f4ba25 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/pinctrl/mt8195-pinfunc.h>
#include <dt-bindings/power/mt8195-power.h>
#include <dt-bindings/reset/mt8195-resets.h>
+#include <dt-bindings/thermal/mediatek-lvts.h>
/ {
compatible = "mediatek,mt8195";
@@ -954,6 +955,17 @@ spi0: spi@1100a000 {
status = "disabled";
};
+ lvts_ap: thermal-sensor@1100b000 {
+ compatible = "mediatek,mt8195-lvts-ap";
+ reg = <0 0x1100b000 0 0x1000>;
+ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST0_THERM_CTRL_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+
spi1: spi@11010000 {
compatible = "mediatek,mt8195-spi",
"mediatek,mt6765-spi";
@@ -1114,6 +1126,17 @@ mmc2: mmc@11250000 {
status = "disabled";
};
+ lvts_mcu: thermal-sensor@11278000 {
+ compatible = "mediatek,mt8195-lvts-mcu";
+ reg = <0 0x11278000 0 0x1000>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+
xhci1: usb@11290000 {
compatible = "mediatek,mt8195-xhci",
"mediatek,mtk-xhci";
@@ -2387,4 +2410,110 @@ dp_tx: dp-tx@1c600000 {
status = "disabled";
};
};
+
+ thermal_zones: thermal-zones {
+ cpu0-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
+ trips {
+ cpu0_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu1-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU1>;
+ trips {
+ cpu1_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu2-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU2>;
+ trips {
+ cpu2_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu3-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU3>;
+ trips {
+ cpu3_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu4-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU0>;
+ trips {
+ cpu4_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu5-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU1>;
+ trips {
+ cpu5_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu6-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU2>;
+ trips {
+ cpu6_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu7-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU3>;
+ trips {
+ cpu7_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
};
--
2.34.1
From: Balsam CHIHI <[email protected]>
The mt8195 SoC has several hotspots around the CPUs. Specify the
targeted temperature threshold when to apply the mitigation and define
the associated cooling devices.
Signed-off-by: Balsam CHIHI <[email protected]>
---
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 169 ++++++++++++++++++++---
1 file changed, 153 insertions(+), 16 deletions(-)
diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
index 636676f4ba25..9544ae91379a 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/pinctrl/mt8195-pinfunc.h>
#include <dt-bindings/power/mt8195-power.h>
#include <dt-bindings/reset/mt8195-resets.h>
+#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/thermal/mediatek-lvts.h>
/ {
@@ -2413,107 +2414,243 @@ dp_tx: dp-tx@1c600000 {
thermal_zones: thermal-zones {
cpu0-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
+
trips {
+ cpu0_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu0_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu0_alert>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu1-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU1>;
+
trips {
+ cpu1_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu1_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu1_alert>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu2-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU2>;
+
trips {
+ cpu2_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu2_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu2_alert>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu3-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU3>;
+
trips {
+ cpu3_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu3_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu3_alert>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu4-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU0>;
+
trips {
+ cpu4_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu4_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu4_alert>;
+ cooling-device = <&cpu4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu5-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU1>;
+
trips {
+ cpu5_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu5_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu5_alert>;
+ cooling-device = <&cpu4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu6-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU2>;
+
trips {
+ cpu6_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu6_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu6_alert>;
+ cooling-device = <&cpu4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
cpu7-thermal {
- polling-delay = <0>;
- polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU3>;
+
trips {
+ cpu7_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
cpu7_crit: trip-crit {
temperature = <100000>;
hysteresis = <2000>;
type = "critical";
};
};
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu7_alert>;
+ cooling-device = <&cpu4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
};
};
};
--
2.34.1
Il 24/01/23 14:17, [email protected] ha scritto:
> From: Balsam CHIHI <[email protected]>
>
> The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
> controllers contained in a thermal domain.
>
> A thermal domains can be the MCU or the AP.
>
> Each thermal domains contain up to seven controllers, each thermal
> controller handle up to four thermal sensors.
>
> The LVTS has two Finite State Machines (FSM), one to handle the
> functionin temperatures range like hot or cold temperature and another
> one to handle monitoring trip point. The FSM notifies via interrupts
> when a trip point is crossed.
>
> The interrupt is managed at the thermal controller level, so when an
> interrupt occurs, the driver has to find out which sensor triggered
> such an interrupt.
>
> The sampling of the thermal can be filtered or immediate. For the
> former, the LVTS measures several points and applies a low pass
> filter.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
On MT8195 Tomato Chromebook:
Tested-by: AngeloGioacchino Del Regno <[email protected]>
Il 24/01/23 14:17, [email protected] ha scritto:
> From: Balsam CHIHI <[email protected]>
>
> The mt8195 SoC has several hotspots around the CPUs. Specify the
> targeted temperature threshold when to apply the mitigation and define
> the associated cooling devices.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
Commit title prefix
- arm64: dts: mt8195:
or
- arm64: dts: mediatek: mt8195:
otherwise,
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Il 24/01/23 14:17, [email protected] ha scritto:
> From: Balsam CHIHI <[email protected]>
>
> Add thermal zones and thermal nodes for the mt8195.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
You should change the commit title to reflect the same syntax that you can find
in a `git log --oneline arch/arm64/boot/dts/mediatek/`.
arm64: dts: mediatek: mt8195: Add thermal zones and thermal nodes
or
arm64: dts: mt8195: Add thermal zones and thermal nodes
...otherwise:
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
Il 24/01/23 14:17, [email protected] ha scritto:
> From: Balsam CHIHI <[email protected]>
>
> Add MediaTek proprietary folder to upstream more thermal zone and cooler
> drivers, relocate the original thermal controller driver to it, and rename it
> as "auxadc_thermal.c" to show its purpose more clearly.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
On 24/01/2023 14:17, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> Add LVTS thermal controllers dt-binding definition for mt8195.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
Krzysztof, Rob,
are you ok with these changes ?
> .../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
> include/dt-bindings/thermal/mediatek-lvts.h | 19 ++++
> 2 files changed, 126 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
>
> diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> new file mode 100644
> index 000000000000..12bfbdd8ff89
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
> +
> +maintainers:
> + - Balsam CHIHI <[email protected]>
> +
> +description: |
> + LVTS is a thermal management architecture composed of three subsystems,
> + a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
> + a Converter - Low Voltage Thermal Sensor converter (LVTS), and
> + a Digital controller (LVTS_CTRL).
> +
> +properties:
> + compatible:
> + enum:
> + - mediatek,mt8195-lvts-ap
> + - mediatek,mt8195-lvts-mcu
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 1
> +
> + resets:
> + maxItems: 1
> + description: LVTS reset for clearing temporary data on AP/MCU.
> +
> + nvmem-cells:
> + minItems: 1
> + items:
> + - description: Calibration eFuse data 1 for LVTS
> + - description: Calibration eFuse data 2 for LVTS
> +
> + nvmem-cell-names:
> + minItems: 1
> + items:
> + - const: lvts-calib-data-1
> + - const: lvts-calib-data-2
> +
> + "#thermal-sensor-cells":
> + const: 1
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - resets
> + - nvmem-cells
> + - nvmem-cell-names
> + - "#thermal-sensor-cells"
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/clock/mt8195-clk.h>
> + #include <dt-bindings/reset/mt8195-resets.h>
> + #include <dt-bindings/thermal/mediatek-lvts.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + lvts_mcu: thermal-sensor@11278000 {
> + compatible = "mediatek,mt8195-lvts-mcu";
> + reg = <0 0x11278000 0 0x1000>;
> + interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
> + clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
> + resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
> + nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
> + nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
> + #thermal-sensor-cells = <1>;
> + };
> + };
> +
> + thermal_zones: thermal-zones {
> + cpu0-thermal {
> + polling-delay = <1000>;
> + polling-delay-passive = <250>;
> + thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
> +
> + trips {
> + cpu0_alert: trip-alert {
> + temperature = <85000>;
> + hysteresis = <2000>;
> + type = "passive";
> + };
> +
> + cpu0_crit: trip-crit {
> + temperature = <100000>;
> + hysteresis = <2000>;
> + type = "critical";
> + };
> + };
> + };
> + };
> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> new file mode 100644
> index 000000000000..428a95c18509
> --- /dev/null
> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + * Author: Balsam CHIHI <[email protected]>
> + */
> +
> +#ifndef __MEDIATEK_LVTS_DT_H
> +#define __MEDIATEK_LVTS_DT_H
> +
> +#define MT8195_MCU_BIG_CPU0 0
> +#define MT8195_MCU_BIG_CPU1 1
> +#define MT8195_MCU_BIG_CPU2 2
> +#define MT8195_MCU_BIG_CPU3 3
> +#define MT8195_MCU_LITTLE_CPU0 4
> +#define MT8195_MCU_LITTLE_CPU1 5
> +#define MT8195_MCU_LITTLE_CPU2 6
> +#define MT8195_MCU_LITTLE_CPU3 7
> +
> +#endif /* __MEDIATEK_LVTS_DT_H */
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
On 24/01/2023 14:17, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> Add efuse node.
> This will be required by the thermal driver to get the calibration data.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
> Reviewed-by: Matthias Brugger <[email protected]>
> Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
I fixed the subject prefix to:
arm64: dts: mt8195: ...
to be in line with other commits.
Please take that into account for future patches you send.
Applied thanks!
> ---
> arch/arm64/boot/dts/mediatek/mt8195.dtsi | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> index 5d31536f4c48..09df105f4606 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> @@ -1380,6 +1380,12 @@ pciephy_glb_intr: pciephy-glb-intr@193 {
> dp_calibration: dp-data@1ac {
> reg = <0x1ac 0x10>;
> };
> + lvts_efuse_data1: lvts1-calib@1bc {
> + reg = <0x1bc 0x14>;
> + };
> + lvts_efuse_data2: lvts2-calib@1d0 {
> + reg = <0x1d0 0x38>;
> + };
> };
>
> u3phy2: t-phy@11c40000 {
On Tue, Jan 24, 2023 at 4:37 PM AngeloGioacchino Del Regno
<[email protected]> wrote:
>
> Il 24/01/23 14:17, [email protected] ha scritto:
> > From: Balsam CHIHI <[email protected]>
> >
> > Add MediaTek proprietary folder to upstream more thermal zone and cooler
> > drivers, relocate the original thermal controller driver to it, and rename it
> > as "auxadc_thermal.c" to show its purpose more clearly.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
>
Hi Angelo,
Thank you.
Best regards,
Balsam
> Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
>
>
On Wed, Jan 25, 2023 at 3:25 PM Matthias Brugger <[email protected]> wrote:
>
>
>
> On 24/01/2023 14:17, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
> > Add efuse node.
> > This will be required by the thermal driver to get the calibration data.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
> > Reviewed-by: Matthias Brugger <[email protected]>
> > Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
>
> I fixed the subject prefix to:
> arm64: dts: mt8195: ...
> to be in line with other commits.
> Please take that into account for future patches you send.
>
> Applied thanks!
>
Hi Matthias,
Thank you for the fix and for applying the patch!
Best regards,
Balsam
> > ---
> > arch/arm64/boot/dts/mediatek/mt8195.dtsi | 6 ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> > index 5d31536f4c48..09df105f4606 100644
> > --- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> > +++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> > @@ -1380,6 +1380,12 @@ pciephy_glb_intr: pciephy-glb-intr@193 {
> > dp_calibration: dp-data@1ac {
> > reg = <0x1ac 0x10>;
> > };
> > + lvts_efuse_data1: lvts1-calib@1bc {
> > + reg = <0x1bc 0x14>;
> > + };
> > + lvts_efuse_data2: lvts2-calib@1d0 {
> > + reg = <0x1d0 0x38>;
> > + };
> > };
> >
> > u3phy2: t-phy@11c40000 {
On Tue, Jan 24, 2023 at 4:31 PM AngeloGioacchino Del Regno
<[email protected]> wrote:
>
> Il 24/01/23 14:17, [email protected] ha scritto:
> > From: Balsam CHIHI <[email protected]>
> >
> > The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
> > controllers contained in a thermal domain.
> >
> > A thermal domains can be the MCU or the AP.
> >
> > Each thermal domains contain up to seven controllers, each thermal
> > controller handle up to four thermal sensors.
> >
> > The LVTS has two Finite State Machines (FSM), one to handle the
> > functionin temperatures range like hot or cold temperature and another
> > one to handle monitoring trip point. The FSM notifies via interrupts
> > when a trip point is crossed.
> >
> > The interrupt is managed at the thermal controller level, so when an
> > interrupt occurs, the driver has to find out which sensor triggered
> > such an interrupt.
> >
> > The sampling of the thermal can be filtered or immediate. For the
> > former, the LVTS measures several points and applies a low pass
> > filter.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
>
> Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
>
> On MT8195 Tomato Chromebook:
> Tested-by: AngeloGioacchino Del Regno <[email protected]>
Hi Angelo,
Thank you for reviewing and testing it.
Best regards,
Balsam
On Tue, Jan 24, 2023 at 4:37 PM AngeloGioacchino Del Regno
<[email protected]> wrote:
>
> Il 24/01/23 14:17, [email protected] ha scritto:
> > From: Balsam CHIHI <[email protected]>
> >
> > Add thermal zones and thermal nodes for the mt8195.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
>
> You should change the commit title to reflect the same syntax that you can find
> in a `git log --oneline arch/arm64/boot/dts/mediatek/`.
>
> arm64: dts: mediatek: mt8195: Add thermal zones and thermal nodes
>
> or
>
> arm64: dts: mt8195: Add thermal zones and thermal nodes
>
Hi Angelo,
Thank you for the review.
Should I resend with the fix?
Or you would fix it when applying it like Matthias did for "[PATCH v11
3/6] arm64/dts/mt8195: Add efuse node to mt8195"?
Best regards,
Balsam
>
> ...otherwise:
>
> Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
>
On 25/01/2023 16:10, Balsam CHIHI wrote:
> On Tue, Jan 24, 2023 at 4:37 PM AngeloGioacchino Del Regno
> <[email protected]> wrote:
>>
>> Il 24/01/23 14:17, [email protected] ha scritto:
>>> From: Balsam CHIHI <[email protected]>
>>>
>>> Add thermal zones and thermal nodes for the mt8195.
>>>
>>> Signed-off-by: Balsam CHIHI <[email protected]>
>>
>> You should change the commit title to reflect the same syntax that you can find
>> in a `git log --oneline arch/arm64/boot/dts/mediatek/`.
>>
>> arm64: dts: mediatek: mt8195: Add thermal zones and thermal nodes
>>
>> or
>>
>> arm64: dts: mt8195: Add thermal zones and thermal nodes
>>
>
> Hi Angelo,
>
> Thank you for the review.
> Should I resend with the fix?
> Or you would fix it when applying it like Matthias did for "[PATCH v11
> 3/6] arm64/dts/mt8195: Add efuse node to mt8195"?
>
If you need to send a new version for any reason, then please update. Otherwise
I'll fix it once I can take it.
Regards,
Matthias
On Tue, Jan 24, 2023 at 02:17:13PM +0100, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
dt-bindings: thermal: ... for the subject
> Add LVTS thermal controllers dt-binding definition for mt8195.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> .../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
> include/dt-bindings/thermal/mediatek-lvts.h | 19 ++++
> 2 files changed, 126 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
>
> diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> new file mode 100644
> index 000000000000..12bfbdd8ff89
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
> +
> +maintainers:
> + - Balsam CHIHI <[email protected]>
> +
> +description: |
> + LVTS is a thermal management architecture composed of three subsystems,
> + a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
> + a Converter - Low Voltage Thermal Sensor converter (LVTS), and
> + a Digital controller (LVTS_CTRL).
> +
> +properties:
> + compatible:
> + enum:
> + - mediatek,mt8195-lvts-ap
> + - mediatek,mt8195-lvts-mcu
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 1
> +
> + resets:
> + maxItems: 1
> + description: LVTS reset for clearing temporary data on AP/MCU.
> +
> + nvmem-cells:
> + minItems: 1
> + items:
> + - description: Calibration eFuse data 1 for LVTS
> + - description: Calibration eFuse data 2 for LVTS
> +
> + nvmem-cell-names:
> + minItems: 1
> + items:
> + - const: lvts-calib-data-1
> + - const: lvts-calib-data-2
> +
> + "#thermal-sensor-cells":
> + const: 1
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - resets
> + - nvmem-cells
> + - nvmem-cell-names
> + - "#thermal-sensor-cells"
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/clock/mt8195-clk.h>
> + #include <dt-bindings/reset/mt8195-resets.h>
> + #include <dt-bindings/thermal/mediatek-lvts.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + lvts_mcu: thermal-sensor@11278000 {
> + compatible = "mediatek,mt8195-lvts-mcu";
> + reg = <0 0x11278000 0 0x1000>;
> + interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
> + clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
> + resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
> + nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
> + nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
> + #thermal-sensor-cells = <1>;
> + };
> + };
> +
> + thermal_zones: thermal-zones {
> + cpu0-thermal {
> + polling-delay = <1000>;
> + polling-delay-passive = <250>;
> + thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
> +
> + trips {
> + cpu0_alert: trip-alert {
> + temperature = <85000>;
> + hysteresis = <2000>;
> + type = "passive";
> + };
> +
> + cpu0_crit: trip-crit {
> + temperature = <100000>;
> + hysteresis = <2000>;
> + type = "critical";
> + };
> + };
> + };
> + };
> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> new file mode 100644
> index 000000000000..428a95c18509
> --- /dev/null
> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
Okay with GPL 4? GPL-2.0-only
Dual license please. Consistent with your .dts files.
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + * Author: Balsam CHIHI <[email protected]>
> + */
> +
> +#ifndef __MEDIATEK_LVTS_DT_H
> +#define __MEDIATEK_LVTS_DT_H
> +
> +#define MT8195_MCU_BIG_CPU0 0
> +#define MT8195_MCU_BIG_CPU1 1
> +#define MT8195_MCU_BIG_CPU2 2
> +#define MT8195_MCU_BIG_CPU3 3
> +#define MT8195_MCU_LITTLE_CPU0 4
> +#define MT8195_MCU_LITTLE_CPU1 5
> +#define MT8195_MCU_LITTLE_CPU2 6
> +#define MT8195_MCU_LITTLE_CPU3 7
> +
> +#endif /* __MEDIATEK_LVTS_DT_H */
> --
> 2.34.1
>
On Wed, Jan 25, 2023 at 12:14:17PM +0100, Daniel Lezcano wrote:
> On 24/01/2023 14:17, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
> > Add LVTS thermal controllers dt-binding definition for mt8195.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
> > ---
>
> Krzysztof, Rob,
>
> are you ok with these changes ?
It says v11, but I sure don't recall the 10 other versions...
Rob
On 25/01/2023 21:35, Rob Herring wrote:
> On Wed, Jan 25, 2023 at 12:14:17PM +0100, Daniel Lezcano wrote:
>> On 24/01/2023 14:17, [email protected] wrote:
>>> From: Balsam CHIHI <[email protected]>
>>>
>>> Add LVTS thermal controllers dt-binding definition for mt8195.
>>>
>>> Signed-off-by: Balsam CHIHI <[email protected]>
>>> ---
>>
>> Krzysztof, Rob,
>>
>> are you ok with these changes ?
>
> It says v11, but I sure don't recall the 10 other versions...
Ah, yes indeed. I remember Krzysztof told Balsam to fix the recipient
list because some maintainers were missing.
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
On Wed, Jan 25, 2023 at 8:09 PM Matthias Brugger <[email protected]> wrote:
>
>
>
> On 25/01/2023 16:10, Balsam CHIHI wrote:
> > On Tue, Jan 24, 2023 at 4:37 PM AngeloGioacchino Del Regno
> > <[email protected]> wrote:
> >>
> >> Il 24/01/23 14:17, [email protected] ha scritto:
> >>> From: Balsam CHIHI <[email protected]>
> >>>
> >>> Add thermal zones and thermal nodes for the mt8195.
> >>>
> >>> Signed-off-by: Balsam CHIHI <[email protected]>
> >>
> >> You should change the commit title to reflect the same syntax that you can find
> >> in a `git log --oneline arch/arm64/boot/dts/mediatek/`.
> >>
> >> arm64: dts: mediatek: mt8195: Add thermal zones and thermal nodes
> >>
> >> or
> >>
> >> arm64: dts: mt8195: Add thermal zones and thermal nodes
> >>
> >
> > Hi Angelo,
> >
> > Thank you for the review.
> > Should I resend with the fix?
> > Or you would fix it when applying it like Matthias did for "[PATCH v11
> > 3/6] arm64/dts/mt8195: Add efuse node to mt8195"?
> >
>
> If you need to send a new version for any reason, then please update. Otherwise
> I'll fix it once I can take it.
Hi Matthias,
OK.
I appreciate it,
thank you.
Best regards,
Balsam
>
> Regards,
> Matthias
Hi Rob,
On Wed, Jan 25, 2023 at 9:34 PM Rob Herring <[email protected]> wrote:
>
> On Tue, Jan 24, 2023 at 02:17:13PM +0100, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
>
> dt-bindings: thermal: ... for the subject
I will fix it.
>
> > Add LVTS thermal controllers dt-binding definition for mt8195.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
> > ---
> > .../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
> > include/dt-bindings/thermal/mediatek-lvts.h | 19 ++++
> > 2 files changed, 126 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> > create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
> >
> > diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> > new file mode 100644
> > index 000000000000..12bfbdd8ff89
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> > @@ -0,0 +1,107 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
> > +
> > +maintainers:
> > + - Balsam CHIHI <[email protected]>
> > +
> > +description: |
> > + LVTS is a thermal management architecture composed of three subsystems,
> > + a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
> > + a Converter - Low Voltage Thermal Sensor converter (LVTS), and
> > + a Digital controller (LVTS_CTRL).
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - mediatek,mt8195-lvts-ap
> > + - mediatek,mt8195-lvts-mcu
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + clocks:
> > + maxItems: 1
> > +
> > + resets:
> > + maxItems: 1
> > + description: LVTS reset for clearing temporary data on AP/MCU.
> > +
> > + nvmem-cells:
> > + minItems: 1
> > + items:
> > + - description: Calibration eFuse data 1 for LVTS
> > + - description: Calibration eFuse data 2 for LVTS
> > +
> > + nvmem-cell-names:
> > + minItems: 1
> > + items:
> > + - const: lvts-calib-data-1
> > + - const: lvts-calib-data-2
> > +
> > + "#thermal-sensor-cells":
> > + const: 1
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - clocks
> > + - resets
> > + - nvmem-cells
> > + - nvmem-cell-names
> > + - "#thermal-sensor-cells"
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > + #include <dt-bindings/clock/mt8195-clk.h>
> > + #include <dt-bindings/reset/mt8195-resets.h>
> > + #include <dt-bindings/thermal/mediatek-lvts.h>
> > +
> > + soc {
> > + #address-cells = <2>;
> > + #size-cells = <2>;
> > +
> > + lvts_mcu: thermal-sensor@11278000 {
> > + compatible = "mediatek,mt8195-lvts-mcu";
> > + reg = <0 0x11278000 0 0x1000>;
> > + interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
> > + clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
> > + resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
> > + nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
> > + nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
> > + #thermal-sensor-cells = <1>;
> > + };
> > + };
> > +
> > + thermal_zones: thermal-zones {
> > + cpu0-thermal {
> > + polling-delay = <1000>;
> > + polling-delay-passive = <250>;
> > + thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
> > +
> > + trips {
> > + cpu0_alert: trip-alert {
> > + temperature = <85000>;
> > + hysteresis = <2000>;
> > + type = "passive";
> > + };
> > +
> > + cpu0_crit: trip-crit {
> > + temperature = <100000>;
> > + hysteresis = <2000>;
> > + type = "critical";
> > + };
> > + };
> > + };
> > + };
> > diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> > new file mode 100644
> > index 000000000000..428a95c18509
> > --- /dev/null
> > +++ b/include/dt-bindings/thermal/mediatek-lvts.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
>
> Okay with GPL 4? GPL-2.0-only
>
> Dual license please. Consistent with your .dts files.
I will fix this too.
>
> > +/*
> > + * Copyright (c) 2023 MediaTek Inc.
> > + * Author: Balsam CHIHI <[email protected]>
> > + */
> > +
> > +#ifndef __MEDIATEK_LVTS_DT_H
> > +#define __MEDIATEK_LVTS_DT_H
> > +
> > +#define MT8195_MCU_BIG_CPU0 0
> > +#define MT8195_MCU_BIG_CPU1 1
> > +#define MT8195_MCU_BIG_CPU2 2
> > +#define MT8195_MCU_BIG_CPU3 3
> > +#define MT8195_MCU_LITTLE_CPU0 4
> > +#define MT8195_MCU_LITTLE_CPU1 5
> > +#define MT8195_MCU_LITTLE_CPU2 6
> > +#define MT8195_MCU_LITTLE_CPU3 7
> > +
> > +#endif /* __MEDIATEK_LVTS_DT_H */
> > --
> > 2.34.1
> >
Thank you for the review.
Best regards,
Balsam
From: Balsam CHIHI <[email protected]>
Add LVTS thermal controllers dt-binding definition for mt8195.
Signed-off-by: Balsam CHIHI <[email protected]>
---
Changelog:
v12:
- Fixed subject prefix
- Fixed licences GPL-2.0+ to GPL-2.0
- Added dual licenses
v11:
- Rebase on top of "thermal/linux-next" :
base=0d568e144ead70189e7f16066dcb155b78ff9266
- Remove unsupported SoC (mt8192) from dt-binding definition
v10:
- Rebase on top of "thermal/linux-next" : thermal-v6.3-rc1
v9:
- Rebase on top of 6.0.0-rc1
- Update dt-bindings :
- Add "allOf:if:then:"
- Use mt8192 as example (instead of mt8195)
- Fix dt-binding errors
- Fix DTS errors
v8:
- Fix coding style issues
- Rebase on top of next-20220803
- Add multi-instance support :
- Rewrite DT-binding and DTS :
- Add DT-binding and DTS for LVTS_v4 (MT8192 and MT8195)
- One LVTS node for each HW Domain (AP and MCU)
- One SW Instance for each HW Domain
v7:
- Fix coding style issues
- Rewrite dt bindings
- was not accurate
- Use mt8195 for example (instead of mt8192)
- Rename mt6873 to mt8192
- Remove clock name
---
---
.../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
include/dt-bindings/thermal/mediatek-lvts.h | 19 ++++
2 files changed, 126 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
new file mode 100644
index 000000000000..12bfbdd8ff89
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
+
+maintainers:
+ - Balsam CHIHI <[email protected]>
+
+description: |
+ LVTS is a thermal management architecture composed of three subsystems,
+ a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
+ a Converter - Low Voltage Thermal Sensor converter (LVTS), and
+ a Digital controller (LVTS_CTRL).
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8195-lvts-ap
+ - mediatek,mt8195-lvts-mcu
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+ description: LVTS reset for clearing temporary data on AP/MCU.
+
+ nvmem-cells:
+ minItems: 1
+ items:
+ - description: Calibration eFuse data 1 for LVTS
+ - description: Calibration eFuse data 2 for LVTS
+
+ nvmem-cell-names:
+ minItems: 1
+ items:
+ - const: lvts-calib-data-1
+ - const: lvts-calib-data-2
+
+ "#thermal-sensor-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - resets
+ - nvmem-cells
+ - nvmem-cell-names
+ - "#thermal-sensor-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8195-clk.h>
+ #include <dt-bindings/reset/mt8195-resets.h>
+ #include <dt-bindings/thermal/mediatek-lvts.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ lvts_mcu: thermal-sensor@11278000 {
+ compatible = "mediatek,mt8195-lvts-mcu";
+ reg = <0 0x11278000 0 0x1000>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+ };
+
+ thermal_zones: thermal-zones {
+ cpu0-thermal {
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
+
+ trips {
+ cpu0_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu0_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
new file mode 100644
index 000000000000..902d5b1e4f43
--- /dev/null
+++ b/include/dt-bindings/thermal/mediatek-lvts.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Balsam CHIHI <[email protected]>
+ */
+
+#ifndef __MEDIATEK_LVTS_DT_H
+#define __MEDIATEK_LVTS_DT_H
+
+#define MT8195_MCU_BIG_CPU0 0
+#define MT8195_MCU_BIG_CPU1 1
+#define MT8195_MCU_BIG_CPU2 2
+#define MT8195_MCU_BIG_CPU3 3
+#define MT8195_MCU_LITTLE_CPU0 4
+#define MT8195_MCU_LITTLE_CPU1 5
+#define MT8195_MCU_LITTLE_CPU2 6
+#define MT8195_MCU_LITTLE_CPU3 7
+
+#endif /* __MEDIATEK_LVTS_DT_H */
--
2.34.1
Hi Rob,
I think Balsam took into account your comments. Is it fine for you ?
On 26/01/2023 17:10, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> Add LVTS thermal controllers dt-binding definition for mt8195.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> Changelog:
> v12:
> - Fixed subject prefix
> - Fixed licences GPL-2.0+ to GPL-2.0
> - Added dual licenses
> v11:
> - Rebase on top of "thermal/linux-next" :
> base=0d568e144ead70189e7f16066dcb155b78ff9266
> - Remove unsupported SoC (mt8192) from dt-binding definition
> v10:
> - Rebase on top of "thermal/linux-next" : thermal-v6.3-rc1
> v9:
> - Rebase on top of 6.0.0-rc1
> - Update dt-bindings :
> - Add "allOf:if:then:"
> - Use mt8192 as example (instead of mt8195)
> - Fix dt-binding errors
> - Fix DTS errors
> v8:
> - Fix coding style issues
> - Rebase on top of next-20220803
> - Add multi-instance support :
> - Rewrite DT-binding and DTS :
> - Add DT-binding and DTS for LVTS_v4 (MT8192 and MT8195)
> - One LVTS node for each HW Domain (AP and MCU)
> - One SW Instance for each HW Domain
> v7:
> - Fix coding style issues
> - Rewrite dt bindings
> - was not accurate
> - Use mt8195 for example (instead of mt8192)
> - Rename mt6873 to mt8192
> - Remove clock name
> ---
> ---
> .../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
> include/dt-bindings/thermal/mediatek-lvts.h | 19 ++++
> 2 files changed, 126 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> create mode 100644 include/dt-bindings/thermal/mediatek-lvts.h
>
> diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> new file mode 100644
> index 000000000000..12bfbdd8ff89
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
> +
> +maintainers:
> + - Balsam CHIHI <[email protected]>
> +
> +description: |
> + LVTS is a thermal management architecture composed of three subsystems,
> + a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
> + a Converter - Low Voltage Thermal Sensor converter (LVTS), and
> + a Digital controller (LVTS_CTRL).
> +
> +properties:
> + compatible:
> + enum:
> + - mediatek,mt8195-lvts-ap
> + - mediatek,mt8195-lvts-mcu
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 1
> +
> + resets:
> + maxItems: 1
> + description: LVTS reset for clearing temporary data on AP/MCU.
> +
> + nvmem-cells:
> + minItems: 1
> + items:
> + - description: Calibration eFuse data 1 for LVTS
> + - description: Calibration eFuse data 2 for LVTS
> +
> + nvmem-cell-names:
> + minItems: 1
> + items:
> + - const: lvts-calib-data-1
> + - const: lvts-calib-data-2
> +
> + "#thermal-sensor-cells":
> + const: 1
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - resets
> + - nvmem-cells
> + - nvmem-cell-names
> + - "#thermal-sensor-cells"
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/clock/mt8195-clk.h>
> + #include <dt-bindings/reset/mt8195-resets.h>
> + #include <dt-bindings/thermal/mediatek-lvts.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + lvts_mcu: thermal-sensor@11278000 {
> + compatible = "mediatek,mt8195-lvts-mcu";
> + reg = <0 0x11278000 0 0x1000>;
> + interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
> + clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
> + resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
> + nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
> + nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
> + #thermal-sensor-cells = <1>;
> + };
> + };
> +
> + thermal_zones: thermal-zones {
> + cpu0-thermal {
> + polling-delay = <1000>;
> + polling-delay-passive = <250>;
> + thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
> +
> + trips {
> + cpu0_alert: trip-alert {
> + temperature = <85000>;
> + hysteresis = <2000>;
> + type = "passive";
> + };
> +
> + cpu0_crit: trip-crit {
> + temperature = <100000>;
> + hysteresis = <2000>;
> + type = "critical";
> + };
> + };
> + };
> + };
> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> new file mode 100644
> index 000000000000..902d5b1e4f43
> --- /dev/null
> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + * Author: Balsam CHIHI <[email protected]>
> + */
> +
> +#ifndef __MEDIATEK_LVTS_DT_H
> +#define __MEDIATEK_LVTS_DT_H
> +
> +#define MT8195_MCU_BIG_CPU0 0
> +#define MT8195_MCU_BIG_CPU1 1
> +#define MT8195_MCU_BIG_CPU2 2
> +#define MT8195_MCU_BIG_CPU3 3
> +#define MT8195_MCU_LITTLE_CPU0 4
> +#define MT8195_MCU_LITTLE_CPU1 5
> +#define MT8195_MCU_LITTLE_CPU2 6
> +#define MT8195_MCU_LITTLE_CPU3 7
> +
> +#endif /* __MEDIATEK_LVTS_DT_H */
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
On 26/01/2023 17:10, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> Add LVTS thermal controllers dt-binding definition for mt8195.
Subject: drop second/last, redundant "dt-binding definition". The
"dt-bindings" prefix is already stating that these are bindings.
Plus two comments at the end.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> Changelog:
> v12:
> - Fixed subject prefix
> - Fixed licences GPL-2.0+ to GPL-2.0
> - Added dual licenses
> + };
> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> new file mode 100644
> index 000000000000..902d5b1e4f43
> --- /dev/null
> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
Same filename as bindings.
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
Although this is correct, any reason why not using exactly the same
license as bindings?
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + * Author: Balsam CHIHI <[email protected]>
> + */
Best regards,
Krzysztof
On 27/01/2023 23:10, Daniel Lezcano wrote:
>
> Hi Rob,
>
> I think Balsam took into account your comments. Is it fine for you ?
>
The patchset was not sent to us at all, so it is the second version we
see. Therefore it's not v12 for us. It's v2 and it still needs fixes.
I replied with minor comments (which could be fixed during applying) and
the license concern (which you rather cannot change while applying).
Best regards,
Krzysztof
Hi Krzysztof,
Thank you for the feedback.
On Sat, Jan 28, 2023 at 11:48 AM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 26/01/2023 17:10, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
> > Add LVTS thermal controllers dt-binding definition for mt8195.
>
> Subject: drop second/last, redundant "dt-binding definition". The
> "dt-bindings" prefix is already stating that these are bindings.
fixed.
The patch title has been fixed as you suggested :
"dt-bindings: thermal: mediatek: Add LVTS thermal controllers"
>
> Plus two comments at the end.
>
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
> > ---
> > Changelog:
> > v12:
> > - Fixed subject prefix
> > - Fixed licences GPL-2.0+ to GPL-2.0
> > - Added dual licenses
>
>
> > + };
> > diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> > new file mode 100644
> > index 000000000000..902d5b1e4f43
> > --- /dev/null
> > +++ b/include/dt-bindings/thermal/mediatek-lvts.h
>
> Same filename as bindings.
fixed.
rename :
include/dt-bindings/thermal/mediatek-lvts.h =>
include/dt-bindings/thermal/mediatek-lvts-thermal.h
>
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
>
> Although this is correct, any reason why not using exactly the same
> license as bindings?
fixed.
both files are now using the same license :
"SPDX-License-Identifier: (GPL-2.0 or MIT)"
>
> > +/*
> > + * Copyright (c) 2023 MediaTek Inc.
> > + * Author: Balsam CHIHI <[email protected]>
> > + */
>
> Best regards,
> Krzysztof
>
I'll send the changes soon.
Best regards,
Balsam
Hi Krzysztof,
On Sat, Jan 28, 2023 at 11:50 AM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 27/01/2023 23:10, Daniel Lezcano wrote:
> >
> > Hi Rob,
> >
> > I think Balsam took into account your comments. Is it fine for you ?
> >
>
> The patchset was not sent to us at all, so it is the second version we
> see. Therefore it's not v12 for us. It's v2 and it still needs fixes.
>
> I replied with minor comments (which could be fixed during applying) and
> the license concern (which you rather cannot change while applying).
I apologize for forgetting to add some email addresses while sending
previous versions.
The changes you asked in your preview comment are taken in account and
ready to be sent.
Please let me know what version number should the patch have.
>
> Best regards,
> Krzysztof
>
Best regards,
Balsam
On 30/01/2023 11:40, Balsam CHIHI wrote:
> Hi Krzysztof,
>
> Thank you for the feedback.
>
> On Sat, Jan 28, 2023 at 11:48 AM Krzysztof Kozlowski
> <[email protected]> wrote:
>>
>> On 26/01/2023 17:10, [email protected] wrote:
>>> From: Balsam CHIHI <[email protected]>
>>>
>>> Add LVTS thermal controllers dt-binding definition for mt8195.
>>
>> Subject: drop second/last, redundant "dt-binding definition". The
>> "dt-bindings" prefix is already stating that these are bindings.
>
> fixed.
> The patch title has been fixed as you suggested :
> "dt-bindings: thermal: mediatek: Add LVTS thermal controllers"
>
>>
>> Plus two comments at the end.
>>
>>>
>>> Signed-off-by: Balsam CHIHI <[email protected]>
>>> ---
>>> Changelog:
>>> v12:
>>> - Fixed subject prefix
>>> - Fixed licences GPL-2.0+ to GPL-2.0
>>> - Added dual licenses
>>
>>
>>> + };
>>> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
>>> new file mode 100644
>>> index 000000000000..902d5b1e4f43
>>> --- /dev/null
>>> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
>>
>> Same filename as bindings.
>
> fixed.
> rename :
> include/dt-bindings/thermal/mediatek-lvts.h =>
> include/dt-bindings/thermal/mediatek-lvts-thermal.h
>
I think it should be
include/dt-bindings/thermal/mediatek,lvts-thermal.yaml
Regards,
Matthias
>>
>>> @@ -0,0 +1,19 @@
>>> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
>>
>> Although this is correct, any reason why not using exactly the same
>> license as bindings?
>
> fixed.
> both files are now using the same license :
> "SPDX-License-Identifier: (GPL-2.0 or MIT)"
>
>>
>>> +/*
>>> + * Copyright (c) 2023 MediaTek Inc.
>>> + * Author: Balsam CHIHI <[email protected]>
>>> + */
>>
>> Best regards,
>> Krzysztof
>>
>
> I'll send the changes soon.
>
> Best regards,
> Balsam
Hi Matthias,
On Mon, Jan 30, 2023 at 12:18 PM Matthias Brugger
<[email protected]> wrote:
>
>
>
> On 30/01/2023 11:40, Balsam CHIHI wrote:
> > Hi Krzysztof,
> >
> > Thank you for the feedback.
> >
> > On Sat, Jan 28, 2023 at 11:48 AM Krzysztof Kozlowski
> > <[email protected]> wrote:
> >>
> >> On 26/01/2023 17:10, [email protected] wrote:
> >>> From: Balsam CHIHI <[email protected]>
> >>>
> >>> Add LVTS thermal controllers dt-binding definition for mt8195.
> >>
> >> Subject: drop second/last, redundant "dt-binding definition". The
> >> "dt-bindings" prefix is already stating that these are bindings.
> >
> > fixed.
> > The patch title has been fixed as you suggested :
> > "dt-bindings: thermal: mediatek: Add LVTS thermal controllers"
> >
> >>
> >> Plus two comments at the end.
> >>
> >>>
> >>> Signed-off-by: Balsam CHIHI <[email protected]>
> >>> ---
> >>> Changelog:
> >>> v12:
> >>> - Fixed subject prefix
> >>> - Fixed licences GPL-2.0+ to GPL-2.0
> >>> - Added dual licenses
> >>
> >>
> >>> + };
> >>> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
> >>> new file mode 100644
> >>> index 000000000000..902d5b1e4f43
> >>> --- /dev/null
> >>> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
> >>
> >> Same filename as bindings.
> >
> > fixed.
> > rename :
> > include/dt-bindings/thermal/mediatek-lvts.h =>
> > include/dt-bindings/thermal/mediatek-lvts-thermal.h
> >
>
> I think it should be
> include/dt-bindings/thermal/mediatek,lvts-thermal.yaml
OK,
I will change it like that.
(".h" and not ".yaml" I believe (just to be sure).).
>
> Regards,
> Matthias
>
> >>
> >>> @@ -0,0 +1,19 @@
> >>> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
> >>
> >> Although this is correct, any reason why not using exactly the same
> >> license as bindings?
> >
> > fixed.
> > both files are now using the same license :
> > "SPDX-License-Identifier: (GPL-2.0 or MIT)"
> >
> >>
> >>> +/*
> >>> + * Copyright (c) 2023 MediaTek Inc.
> >>> + * Author: Balsam CHIHI <[email protected]>
> >>> + */
> >>
> >> Best regards,
> >> Krzysztof
> >>
> >
> > I'll send the changes soon.
> >
> > Best regards,
> > Balsam
Best regards,
Balsam
On 30/01/2023 13:19, Balsam CHIHI wrote:
> Hi Matthias,
>
> On Mon, Jan 30, 2023 at 12:18 PM Matthias Brugger
> <[email protected]> wrote:
>>
>>
>>
>> On 30/01/2023 11:40, Balsam CHIHI wrote:
>>> Hi Krzysztof,
>>>
>>> Thank you for the feedback.
>>>
>>> On Sat, Jan 28, 2023 at 11:48 AM Krzysztof Kozlowski
>>> <[email protected]> wrote:
>>>>
>>>> On 26/01/2023 17:10, [email protected] wrote:
>>>>> From: Balsam CHIHI <[email protected]>
>>>>>
>>>>> Add LVTS thermal controllers dt-binding definition for mt8195.
>>>>
>>>> Subject: drop second/last, redundant "dt-binding definition". The
>>>> "dt-bindings" prefix is already stating that these are bindings.
>>>
>>> fixed.
>>> The patch title has been fixed as you suggested :
>>> "dt-bindings: thermal: mediatek: Add LVTS thermal controllers"
>>>
>>>>
>>>> Plus two comments at the end.
>>>>
>>>>>
>>>>> Signed-off-by: Balsam CHIHI <[email protected]>
>>>>> ---
>>>>> Changelog:
>>>>> v12:
>>>>> - Fixed subject prefix
>>>>> - Fixed licences GPL-2.0+ to GPL-2.0
>>>>> - Added dual licenses
>>>>
>>>>
>>>>> + };
>>>>> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
>>>>> new file mode 100644
>>>>> index 000000000000..902d5b1e4f43
>>>>> --- /dev/null
>>>>> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
>>>>
>>>> Same filename as bindings.
>>>
>>> fixed.
>>> rename :
>>> include/dt-bindings/thermal/mediatek-lvts.h =>
>>> include/dt-bindings/thermal/mediatek-lvts-thermal.h
>>>
>>
>> I think it should be
>> include/dt-bindings/thermal/mediatek,lvts-thermal.yaml
>
> OK,
> I will change it like that.
> (".h" and not ".yaml" I believe (just to be sure).).
>
Yes sure, .h. Sorry for the confusion.
Regards,
Matthias
>>
>> Regards,
>> Matthias
>>
>>>>
>>>>> @@ -0,0 +1,19 @@
>>>>> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
>>>>
>>>> Although this is correct, any reason why not using exactly the same
>>>> license as bindings?
>>>
>>> fixed.
>>> both files are now using the same license :
>>> "SPDX-License-Identifier: (GPL-2.0 or MIT)"
>>>
>>>>
>>>>> +/*
>>>>> + * Copyright (c) 2023 MediaTek Inc.
>>>>> + * Author: Balsam CHIHI <[email protected]>
>>>>> + */
>>>>
>>>> Best regards,
>>>> Krzysztof
>>>>
>>>
>>> I'll send the changes soon.
>>>
>>> Best regards,
>>> Balsam
>
> Best regards,
> Balsam
From: Balsam CHIHI <[email protected]>
Add LVTS thermal controllers dt-binding definition for mt8195.
Signed-off-by: Balsam CHIHI <[email protected]>
---
Changelog:
v3:
- Fixed subject prefix
- Fixed licenses GPL-2.0-only OR BSD-2-Clause
to GPL-2.0 OR MIT (to match DT)
- Fixed matching dt-binding file names
v2:
- Fixed subject prefix
- Fixed licenses GPL-2.0+ to GPL-2.0
- Added dual licenses
---
---
.../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
.../thermal/mediatek,lvts-thermal.h | 19 ++++
2 files changed, 126 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
create mode 100644 include/dt-bindings/thermal/mediatek,lvts-thermal.h
diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
new file mode 100644
index 000000000000..5fa5c7a1a417
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0 OR MIT)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
+
+maintainers:
+ - Balsam CHIHI <[email protected]>
+
+description: |
+ LVTS is a thermal management architecture composed of three subsystems,
+ a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
+ a Converter - Low Voltage Thermal Sensor converter (LVTS), and
+ a Digital controller (LVTS_CTRL).
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8195-lvts-ap
+ - mediatek,mt8195-lvts-mcu
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+ description: LVTS reset for clearing temporary data on AP/MCU.
+
+ nvmem-cells:
+ minItems: 1
+ items:
+ - description: Calibration eFuse data 1 for LVTS
+ - description: Calibration eFuse data 2 for LVTS
+
+ nvmem-cell-names:
+ minItems: 1
+ items:
+ - const: lvts-calib-data-1
+ - const: lvts-calib-data-2
+
+ "#thermal-sensor-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - resets
+ - nvmem-cells
+ - nvmem-cell-names
+ - "#thermal-sensor-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8195-clk.h>
+ #include <dt-bindings/reset/mt8195-resets.h>
+ #include <dt-bindings/thermal/mediatek,lvts-thermal.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ lvts_mcu: thermal-sensor@11278000 {
+ compatible = "mediatek,mt8195-lvts-mcu";
+ reg = <0 0x11278000 0 0x1000>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+ };
+
+ thermal_zones: thermal-zones {
+ cpu0-thermal {
+ polling-delay = <1000>;
+ polling-delay-passive = <250>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
+
+ trips {
+ cpu0_alert: trip-alert {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu0_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
diff --git a/include/dt-bindings/thermal/mediatek,lvts-thermal.h b/include/dt-bindings/thermal/mediatek,lvts-thermal.h
new file mode 100644
index 000000000000..ca1ef29a8fee
--- /dev/null
+++ b/include/dt-bindings/thermal/mediatek,lvts-thermal.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Balsam CHIHI <[email protected]>
+ */
+
+#ifndef __MEDIATEK_LVTS_DT_H
+#define __MEDIATEK_LVTS_DT_H
+
+#define MT8195_MCU_BIG_CPU0 0
+#define MT8195_MCU_BIG_CPU1 1
+#define MT8195_MCU_BIG_CPU2 2
+#define MT8195_MCU_BIG_CPU3 3
+#define MT8195_MCU_LITTLE_CPU0 4
+#define MT8195_MCU_LITTLE_CPU1 5
+#define MT8195_MCU_LITTLE_CPU2 6
+#define MT8195_MCU_LITTLE_CPU3 7
+
+#endif /* __MEDIATEK_LVTS_DT_H */
--
2.34.1
From: Balsam CHIHI <[email protected]>
Add thermal zones and thermal nodes for the mt8195.
Signed-off-by: Balsam CHIHI <[email protected]>
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
---
Changelog:
v12:
- Fixed subject prefix
- Rename "include/dt-bindings/thermal/mediatek-lvts.h"
to "include/dt-bindings/thermal/mediatek,lvts-thermal.h"
due to this patch
https://lore.kernel.org/all/[email protected]/
---
---
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 129 +++++++++++++++++++++++
1 file changed, 129 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
index 09df105f4606..c7e958f8f1b5 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/pinctrl/mt8195-pinfunc.h>
#include <dt-bindings/power/mt8195-power.h>
#include <dt-bindings/reset/mt8195-resets.h>
+#include <dt-bindings/thermal/mediatek,lvts-thermal.h>
/ {
compatible = "mediatek,mt8195";
@@ -954,6 +955,17 @@ spi0: spi@1100a000 {
status = "disabled";
};
+ lvts_ap: thermal-sensor@1100b000 {
+ compatible = "mediatek,mt8195-lvts-ap";
+ reg = <0 0x1100b000 0 0x1000>;
+ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST0_THERM_CTRL_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+
spi1: spi@11010000 {
compatible = "mediatek,mt8195-spi",
"mediatek,mt6765-spi";
@@ -1114,6 +1126,17 @@ mmc2: mmc@11250000 {
status = "disabled";
};
+ lvts_mcu: thermal-sensor@11278000 {
+ compatible = "mediatek,mt8195-lvts-mcu";
+ reg = <0 0x11278000 0 0x1000>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
+ resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
+ nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
+ nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
+ #thermal-sensor-cells = <1>;
+ };
+
xhci1: usb@11290000 {
compatible = "mediatek,mt8195-xhci",
"mediatek,mtk-xhci";
@@ -2387,4 +2410,110 @@ dp_tx: dp-tx@1c600000 {
status = "disabled";
};
};
+
+ thermal_zones: thermal-zones {
+ cpu0-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
+ trips {
+ cpu0_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu1-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU1>;
+ trips {
+ cpu1_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu2-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU2>;
+ trips {
+ cpu2_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu3-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU3>;
+ trips {
+ cpu3_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu4-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU0>;
+ trips {
+ cpu4_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu5-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU1>;
+ trips {
+ cpu5_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu6-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU2>;
+ trips {
+ cpu6_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ cpu7-thermal {
+ polling-delay = <0>;
+ polling-delay-passive = <0>;
+ thermal-sensors = <&lvts_mcu MT8195_MCU_BIG_CPU3>;
+ trips {
+ cpu7_crit: trip-crit {
+ temperature = <100000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
};
--
2.34.1
From: Balsam CHIHI <[email protected]>
The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
controllers contained in a thermal domain.
A thermal domains can be the MCU or the AP.
Each thermal domains contain up to seven controllers, each thermal
controller handle up to four thermal sensors.
The LVTS has two Finite State Machines (FSM), one to handle the
functionin temperatures range like hot or cold temperature and another
one to handle monitoring trip point. The FSM notifies via interrupts
when a trip point is crossed.
The interrupt is managed at the thermal controller level, so when an
interrupt occurs, the driver has to find out which sensor triggered
such an interrupt.
The sampling of the thermal can be filtered or immediate. For the
former, the LVTS measures several points and applies a low pass
filter.
Signed-off-by: Balsam CHIHI <[email protected]>
Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
On MT8195 Tomato Chromebook:
Tested-by: AngeloGioacchino Del Regno <[email protected]>
---
Changelog:
v12:
- Fixed subject prefix
- Rename "include/dt-bindings/thermal/mediatek-lvts.h"
to "include/dt-bindings/thermal/mediatek,lvts-thermal.h"
due to this patch
https://lore.kernel.org/all/[email protected]/
---
---
drivers/thermal/mediatek/Kconfig | 16 +
drivers/thermal/mediatek/Makefile | 1 +
drivers/thermal/mediatek/lvts_thermal.c | 1261 +++++++++++++++++++++++
3 files changed, 1278 insertions(+)
create mode 100644 drivers/thermal/mediatek/lvts_thermal.c
diff --git a/drivers/thermal/mediatek/Kconfig b/drivers/thermal/mediatek/Kconfig
index 7558a847d4e9..d82c86d9be56 100644
--- a/drivers/thermal/mediatek/Kconfig
+++ b/drivers/thermal/mediatek/Kconfig
@@ -18,4 +18,20 @@ config MTK_SOC_THERMAL
This driver configures thermal controllers to collect
temperature via AUXADC interface.
+config MTK_LVTS_THERMAL
+ tristate "LVTS Thermal Driver for MediaTek SoCs"
+ depends on HAS_IOMEM
+ help
+ Enable this option if you want to get SoC temperature
+ information for supported MediaTek platforms.
+ This driver configures LVTS (Low Voltage Thermal Sensor)
+ thermal controllers to collect temperatures via ASIF
+ (Analog Serial Interface).
+
+config MTK_LVTS_THERMAL_DEBUGFS
+ bool "LVTS thermal debugfs"
+ depends on MTK_LVTS_THERMAL && DEBUG_FS
+ help
+ Enable this option to debug the internals of the device driver.
+
endif
diff --git a/drivers/thermal/mediatek/Makefile b/drivers/thermal/mediatek/Makefile
index 53e86e30b26f..1c6daa1e644b 100644
--- a/drivers/thermal/mediatek/Makefile
+++ b/drivers/thermal/mediatek/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_MTK_SOC_THERMAL) += auxadc_thermal.o
+obj-$(CONFIG_MTK_LVTS_THERMAL) += lvts_thermal.o
diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c
new file mode 100644
index 000000000000..29cb337c38d4
--- /dev/null
+++ b/drivers/thermal/mediatek/lvts_thermal.c
@@ -0,0 +1,1261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ * Author: Balsam CHIHI <[email protected]>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/thermal.h>
+#include <dt-bindings/thermal/mediatek,lvts-thermal.h>
+
+#define LVTS_MONCTL0(__base) (__base + 0x0000)
+#define LVTS_MONCTL1(__base) (__base + 0x0004)
+#define LVTS_MONCTL2(__base) (__base + 0x0008)
+#define LVTS_MONINT(__base) (__base + 0x000C)
+#define LVTS_MONINTSTS(__base) (__base + 0x0010)
+#define LVTS_MONIDET0(__base) (__base + 0x0014)
+#define LVTS_MONIDET1(__base) (__base + 0x0018)
+#define LVTS_MONIDET2(__base) (__base + 0x001C)
+#define LVTS_MONIDET3(__base) (__base + 0x0020)
+#define LVTS_H2NTHRE(__base) (__base + 0x0024)
+#define LVTS_HTHRE(__base) (__base + 0x0028)
+#define LVTS_OFFSETH(__base) (__base + 0x0030)
+#define LVTS_OFFSETL(__base) (__base + 0x0034)
+#define LVTS_MSRCTL0(__base) (__base + 0x0038)
+#define LVTS_MSRCTL1(__base) (__base + 0x003C)
+#define LVTS_TSSEL(__base) (__base + 0x0040)
+#define LVTS_CALSCALE(__base) (__base + 0x0048)
+#define LVTS_ID(__base) (__base + 0x004C)
+#define LVTS_CONFIG(__base) (__base + 0x0050)
+#define LVTS_EDATA00(__base) (__base + 0x0054)
+#define LVTS_EDATA01(__base) (__base + 0x0058)
+#define LVTS_EDATA02(__base) (__base + 0x005C)
+#define LVTS_EDATA03(__base) (__base + 0x0060)
+#define LVTS_MSR0(__base) (__base + 0x0090)
+#define LVTS_MSR1(__base) (__base + 0x0094)
+#define LVTS_MSR2(__base) (__base + 0x0098)
+#define LVTS_MSR3(__base) (__base + 0x009C)
+#define LVTS_IMMD0(__base) (__base + 0x00A0)
+#define LVTS_IMMD1(__base) (__base + 0x00A4)
+#define LVTS_IMMD2(__base) (__base + 0x00A8)
+#define LVTS_IMMD3(__base) (__base + 0x00AC)
+#define LVTS_PROTCTL(__base) (__base + 0x00C0)
+#define LVTS_PROTTA(__base) (__base + 0x00C4)
+#define LVTS_PROTTB(__base) (__base + 0x00C8)
+#define LVTS_PROTTC(__base) (__base + 0x00CC)
+#define LVTS_CLKEN(__base) (__base + 0x00E4)
+
+#define LVTS_PERIOD_UNIT ((118 * 1000) / (256 * 38))
+#define LVTS_GROUP_INTERVAL 1
+#define LVTS_FILTER_INTERVAL 1
+#define LVTS_SENSOR_INTERVAL 1
+#define LVTS_HW_FILTER 0x2
+#define LVTS_TSSEL_CONF 0x13121110
+#define LVTS_CALSCALE_CONF 0x300
+#define LVTS_MONINT_CONF 0x9FBF7BDE
+
+#define LVTS_INT_SENSOR0 0x0009001F
+#define LVTS_INT_SENSOR1 0X000881F0
+#define LVTS_INT_SENSOR2 0x00247C00
+#define LVTS_INT_SENSOR3 0x1FC00000
+
+#define LVTS_SENSOR_MAX 4
+#define LVTS_GOLDEN_TEMP_MAX 62
+#define LVTS_GOLDEN_TEMP_DEFAULT 50
+#define LVTS_COEFF_A -250460
+#define LVTS_COEFF_B 250460
+
+#define LVTS_MSR_IMMEDIATE_MODE 0
+#define LVTS_MSR_FILTERED_MODE 1
+
+#define LVTS_HW_SHUTDOWN_MT8195 105000
+
+static int golden_temp = LVTS_GOLDEN_TEMP_DEFAULT;
+static int coeff_b = LVTS_COEFF_B;
+
+struct lvts_sensor_data {
+ int dt_id;
+};
+
+struct lvts_ctrl_data {
+ struct lvts_sensor_data lvts_sensor[LVTS_SENSOR_MAX];
+ int cal_offset[LVTS_SENSOR_MAX];
+ int hw_tshut_temp;
+ int num_lvts_sensor;
+ int offset;
+ int mode;
+};
+
+struct lvts_data {
+ struct lvts_ctrl_data *lvts_ctrl;
+ int num_lvts_ctrl;
+};
+
+struct lvts_sensor {
+ struct thermal_zone_device *tz;
+ void __iomem *msr;
+ void __iomem *base;
+ int id;
+ int dt_id;
+};
+
+struct lvts_ctrl {
+ struct lvts_sensor sensors[LVTS_SENSOR_MAX];
+ u32 calibration[LVTS_SENSOR_MAX];
+ u32 hw_tshut_raw_temp;
+ int num_lvts_sensor;
+ int mode;
+ void __iomem *base;
+};
+
+struct lvts_domain {
+ struct lvts_ctrl *lvts_ctrl;
+ struct reset_control *reset;
+ struct clk *clk;
+ int num_lvts_ctrl;
+ void __iomem *base;
+ size_t calib_len;
+ u8 *calib;
+};
+
+#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
+
+static struct dentry *root;
+
+#define LVTS_DEBUG_FS_REGS(__reg) \
+{ \
+ .name = __stringify(__reg), \
+ .offset = __reg(0), \
+}
+
+static const struct debugfs_reg32 lvts_regs[] = {
+ LVTS_DEBUG_FS_REGS(LVTS_MONCTL0),
+ LVTS_DEBUG_FS_REGS(LVTS_MONCTL1),
+ LVTS_DEBUG_FS_REGS(LVTS_MONCTL2),
+ LVTS_DEBUG_FS_REGS(LVTS_MONINT),
+ LVTS_DEBUG_FS_REGS(LVTS_MONINTSTS),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET0),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET1),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET2),
+ LVTS_DEBUG_FS_REGS(LVTS_MONIDET3),
+ LVTS_DEBUG_FS_REGS(LVTS_H2NTHRE),
+ LVTS_DEBUG_FS_REGS(LVTS_HTHRE),
+ LVTS_DEBUG_FS_REGS(LVTS_OFFSETH),
+ LVTS_DEBUG_FS_REGS(LVTS_OFFSETL),
+ LVTS_DEBUG_FS_REGS(LVTS_MSRCTL0),
+ LVTS_DEBUG_FS_REGS(LVTS_MSRCTL1),
+ LVTS_DEBUG_FS_REGS(LVTS_TSSEL),
+ LVTS_DEBUG_FS_REGS(LVTS_CALSCALE),
+ LVTS_DEBUG_FS_REGS(LVTS_ID),
+ LVTS_DEBUG_FS_REGS(LVTS_CONFIG),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA00),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA01),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA02),
+ LVTS_DEBUG_FS_REGS(LVTS_EDATA03),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR0),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR1),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR2),
+ LVTS_DEBUG_FS_REGS(LVTS_MSR3),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD0),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD1),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD2),
+ LVTS_DEBUG_FS_REGS(LVTS_IMMD3),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTCTL),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTTA),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTTB),
+ LVTS_DEBUG_FS_REGS(LVTS_PROTTC),
+ LVTS_DEBUG_FS_REGS(LVTS_CLKEN),
+};
+
+static int lvts_debugfs_init(struct device *dev,
+ struct lvts_domain *lvts_td)
+{
+ struct debugfs_regset32 *regset;
+ struct lvts_ctrl *lvts_ctrl;
+ struct dentry *dentry;
+ struct dentry *dom_dentry;
+ char name[64];
+ int i;
+
+ if (!root)
+ root = debugfs_create_dir("lvts", NULL);
+
+ dom_dentry = debugfs_create_dir(dev_name(dev), root);
+ if (!dom_dentry)
+ return 0;
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+
+ lvts_ctrl = &lvts_td->lvts_ctrl[i];
+
+ sprintf(name, "controller%d", i);
+ dentry = debugfs_create_dir(name, dom_dentry);
+ if (!dentry)
+ continue;
+
+ regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL);
+ if (!regset)
+ continue;
+
+ regset->base = lvts_ctrl->base;
+ regset->regs = lvts_regs;
+ regset->nregs = ARRAY_SIZE(lvts_regs);
+
+ debugfs_create_regset32("registers", 0400, dentry, regset);
+ }
+
+ return 0;
+}
+
+static void lvts_debugfs_exit(void)
+{
+ debugfs_remove_recursive(root);
+}
+
+#else
+
+static inline int lvts_debugfs_init(struct device *dev,
+ struct lvts_domain *lvts_td)
+{
+ return 0;
+}
+
+static void lvts_debugfs_exit(void) { }
+
+#endif
+
+static int lvts_raw_to_temp(u32 raw_temp)
+{
+ int temperature;
+
+ temperature = ((s64)(raw_temp & 0xFFFF) * LVTS_COEFF_A) >> 14;
+ temperature += coeff_b;
+
+ return temperature;
+}
+
+static u32 lvts_temp_to_raw(int temperature)
+{
+ u32 raw_temp = ((s64)(coeff_b - temperature)) << 14;
+
+ raw_temp = div_s64(raw_temp, -LVTS_COEFF_A);
+
+ return raw_temp;
+}
+
+static int lvts_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct lvts_sensor *lvts_sensor = tz->devdata;
+ void __iomem *msr = lvts_sensor->msr;
+ u32 value;
+
+ /*
+ * Measurement registers:
+ *
+ * LVTS_MSR[0-3] / LVTS_IMMD[0-3]
+ *
+ * Bits:
+ *
+ * 32-17: Unused
+ * 16 : Valid temperature
+ * 15-0 : Raw temperature
+ */
+ value = readl(msr);
+
+ /*
+ * As the thermal zone temperature will read before the
+ * hardware sensor is fully initialized, we have to check the
+ * validity of the temperature returned when reading the
+ * measurement register. The thermal controller will set the
+ * valid bit temperature only when it is totally initialized.
+ *
+ * Otherwise, we may end up with garbage values out of the
+ * functionning temperature and directly jump to a system
+ * shutdown.
+ */
+ if (!(value & BIT(16)))
+ return -EAGAIN;
+
+ *temp = lvts_raw_to_temp(value & 0xFFFF);
+
+ return 0;
+}
+
+static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high)
+{
+ struct lvts_sensor *lvts_sensor = tz->devdata;
+ void __iomem *base = lvts_sensor->base;
+ u32 raw_low = lvts_temp_to_raw(low);
+ u32 raw_high = lvts_temp_to_raw(high);
+
+ /*
+ * Hot to normal temperature threshold
+ *
+ * LVTS_H2NTHRE
+ *
+ * Bits:
+ *
+ * 14-0 : Raw temperature for threshold
+ */
+ if (low != -INT_MAX) {
+ dev_dbg(&tz->device, "Setting low limit temperature interrupt: %d\n", low);
+ writel(raw_low, LVTS_H2NTHRE(base));
+ }
+
+ /*
+ * Hot temperature threshold
+ *
+ * LVTS_HTHRE
+ *
+ * Bits:
+ *
+ * 14-0 : Raw temperature for threshold
+ */
+ dev_dbg(&tz->device, "Setting high limit temperature interrupt: %d\n", high);
+ writel(raw_high, LVTS_HTHRE(base));
+
+ return 0;
+}
+
+static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl)
+{
+ irqreturn_t iret = IRQ_NONE;
+ u32 value, masks[] = {
+ LVTS_INT_SENSOR0,
+ LVTS_INT_SENSOR1,
+ LVTS_INT_SENSOR2,
+ LVTS_INT_SENSOR3
+ };
+ int i;
+
+ /*
+ * Interrupt monitoring status
+ *
+ * LVTS_MONINTST
+ *
+ * Bits:
+ *
+ * 31 : Interrupt for stage 3
+ * 30 : Interrupt for stage 2
+ * 29 : Interrupt for state 1
+ * 28 : Interrupt using filter on sensor 3
+ *
+ * 27 : Interrupt using immediate on sensor 3
+ * 26 : Interrupt normal to hot on sensor 3
+ * 25 : Interrupt high offset on sensor 3
+ * 24 : Interrupt low offset on sensor 3
+ *
+ * 23 : Interrupt hot threshold on sensor 3
+ * 22 : Interrupt cold threshold on sensor 3
+ * 21 : Interrupt using filter on sensor 2
+ * 20 : Interrupt using filter on sensor 1
+ *
+ * 19 : Interrupt using filter on sensor 0
+ * 18 : Interrupt using immediate on sensor 2
+ * 17 : Interrupt using immediate on sensor 1
+ * 16 : Interrupt using immediate on sensor 0
+ *
+ * 15 : Interrupt device access timeout interrupt
+ * 14 : Interrupt normal to hot on sensor 2
+ * 13 : Interrupt high offset interrupt on sensor 2
+ * 12 : Interrupt low offset interrupt on sensor 2
+ *
+ * 11 : Interrupt hot threshold on sensor 2
+ * 10 : Interrupt cold threshold on sensor 2
+ * 9 : Interrupt normal to hot on sensor 1
+ * 8 : Interrupt high offset interrupt on sensor 1
+ *
+ * 7 : Interrupt low offset interrupt on sensor 1
+ * 6 : Interrupt hot threshold on sensor 1
+ * 5 : Interrupt cold threshold on sensor 1
+ * 4 : Interrupt normal to hot on sensor 0
+ *
+ * 3 : Interrupt high offset interrupt on sensor 0
+ * 2 : Interrupt low offset interrupt on sensor 0
+ * 1 : Interrupt hot threshold on sensor 0
+ * 0 : Interrupt cold threshold on sensor 0
+ *
+ * We are interested in the sensor(s) responsible of the
+ * interrupt event. We update the thermal framework with the
+ * thermal zone associated with the sensor. The framework will
+ * take care of the rest whatever the kind of interrupt, we
+ * are only interested in which sensor raised the interrupt.
+ *
+ * sensor 3 interrupt: 0001 1111 1100 0000 0000 0000 0000 0000
+ * => 0x1FC00000
+ * sensor 2 interrupt: 0000 0000 0010 0100 0111 1100 0000 0000
+ * => 0x00247C00
+ * sensor 1 interrupt: 0000 0000 0001 0001 0000 0011 1110 0000
+ * => 0X000881F0
+ * sensor 0 interrupt: 0000 0000 0000 1001 0000 0000 0001 1111
+ * => 0x0009001F
+ */
+ value = readl(LVTS_MONINTSTS(lvts_ctrl->base));
+
+ /*
+ * Let's figure out which sensors raised the interrupt
+ *
+ * NOTE: the masks array must be ordered with the index
+ * corresponding to the sensor id eg. index=0, mask for
+ * sensor0.
+ */
+ for (i = 0; i < ARRAY_SIZE(masks); i++) {
+
+ if (!(value & masks[i]))
+ continue;
+
+ thermal_zone_device_update(lvts_ctrl->sensors[i].tz,
+ THERMAL_TRIP_VIOLATED);
+ iret = IRQ_HANDLED;
+ }
+
+ /*
+ * Write back to clear the interrupt status (W1C)
+ */
+ writel(value, LVTS_MONINTSTS(lvts_ctrl->base));
+
+ return iret;
+}
+
+/*
+ * Temperature interrupt handler. Even if the driver supports more
+ * interrupt modes, we use the interrupt when the temperature crosses
+ * the hot threshold the way up and the way down (modulo the
+ * hysteresis).
+ *
+ * Each thermal domain has a couple of interrupts, one for hardware
+ * reset and another one for all the thermal events happening on the
+ * different sensors.
+ *
+ * The interrupt is configured for thermal events when crossing the
+ * hot temperature limit. At each interrupt, we check in every
+ * controller if there is an interrupt pending.
+ */
+static irqreturn_t lvts_irq_handler(int irq, void *data)
+{
+ struct lvts_domain *lvts_td = data;
+ irqreturn_t aux, iret = IRQ_NONE;
+ int i;
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+
+ aux = lvts_ctrl_irq_handler(lvts_td->lvts_ctrl);
+ if (aux != IRQ_HANDLED)
+ continue;
+
+ iret = IRQ_HANDLED;
+ }
+
+ return iret;
+}
+
+static struct thermal_zone_device_ops lvts_ops = {
+ .get_temp = lvts_get_temp,
+ .set_trips = lvts_set_trips,
+};
+
+static int __init lvts_sensor_init(struct device *dev,
+ struct lvts_ctrl *lvts_ctrl,
+ struct lvts_ctrl_data *lvts_ctrl_data)
+{
+ struct lvts_sensor *lvts_sensor = lvts_ctrl->sensors;
+ void __iomem *msr_regs[] = {
+ LVTS_MSR0(lvts_ctrl->base),
+ LVTS_MSR1(lvts_ctrl->base),
+ LVTS_MSR2(lvts_ctrl->base),
+ LVTS_MSR3(lvts_ctrl->base)
+ };
+
+ void __iomem *imm_regs[] = {
+ LVTS_IMMD0(lvts_ctrl->base),
+ LVTS_IMMD1(lvts_ctrl->base),
+ LVTS_IMMD2(lvts_ctrl->base),
+ LVTS_IMMD3(lvts_ctrl->base)
+ };
+
+ int i;
+
+ for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) {
+
+ int dt_id = lvts_ctrl_data->lvts_sensor[i].dt_id;
+
+ /*
+ * At this point, we don't know which id matches which
+ * sensor. Let's set arbitrally the id from the index.
+ */
+ lvts_sensor[i].id = i;
+
+ /*
+ * The thermal zone registration will set the trip
+ * point interrupt in the thermal controller
+ * register. But this one will be reset in the
+ * initialization after. So we need to post pone the
+ * thermal zone creation after the controller is
+ * setup. For this reason, we store the device tree
+ * node id from the data in the sensor structure
+ */
+ lvts_sensor[i].dt_id = dt_id;
+
+ /*
+ * We assign the base address of the thermal
+ * controller as a back pointer. So it will be
+ * accessible from the different thermal framework ops
+ * as we pass the lvts_sensor pointer as thermal zone
+ * private data.
+ */
+ lvts_sensor[i].base = lvts_ctrl->base;
+
+ /*
+ * Each sensor has its own register address to read from.
+ */
+ lvts_sensor[i].msr = lvts_ctrl_data->mode == LVTS_MSR_IMMEDIATE_MODE ?
+ imm_regs[i] : msr_regs[i];
+ };
+
+ lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor;
+
+ return 0;
+}
+
+/*
+ * The efuse blob values follows the sensor enumeration per thermal
+ * controller. The decoding of the stream is as follow:
+ *
+ * <--?-> <----big0 ???---> <-sensor0-> <-0->
+ * ------------------------------------------
+ * index in the stream: : | 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 |
+ * ------------------------------------------
+ *
+ * <--sensor1--><-0-> <----big1 ???---> <-sen
+ * ------------------------------------------
+ * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
+ * ------------------------------------------
+ *
+ * sor0-> <-0-> <-sensor1-> <-0-> ..........
+ * ------------------------------------------
+ * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
+ * ------------------------------------------
+ *
+ * And so on ...
+ *
+ * The data description gives the offset of the calibration data in
+ * this bytes stream for each sensor.
+ *
+ * Each thermal controller can handle up to 4 sensors max, we don't
+ * care if there are less as the array of calibration is sized to 4
+ * anyway. The unused sensor slot will be zeroed.
+ */
+static int __init lvts_calibration_init(struct device *dev,
+ struct lvts_ctrl *lvts_ctrl,
+ struct lvts_ctrl_data *lvts_ctrl_data,
+ u8 *efuse_calibration)
+{
+ int i;
+
+ for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++)
+ memcpy(&lvts_ctrl->calibration[i],
+ efuse_calibration + lvts_ctrl_data->cal_offset[i], 2);
+
+ return 0;
+}
+
+/*
+ * The efuse bytes stream can be split into different chunk of
+ * nvmems. This function reads and concatenate those into a single
+ * buffer so it can be read sequentially when initializing the
+ * calibration data.
+ */
+static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td,
+ struct lvts_data *lvts_data)
+{
+ struct device_node *np = dev_of_node(dev);
+ struct nvmem_cell *cell;
+ struct property *prop;
+ const char *cell_name;
+
+ of_property_for_each_string(np, "nvmem-cell-names", prop, cell_name) {
+ size_t len;
+ u8 *efuse;
+
+ cell = of_nvmem_cell_get(np, cell_name);
+ if (IS_ERR(cell)) {
+ dev_dbg(dev, "Failed to get cell '%s'\n", cell_name);
+ return PTR_ERR(cell);
+ }
+
+ efuse = nvmem_cell_read(cell, &len);
+
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(efuse)) {
+ dev_dbg(dev, "Failed to read cell '%s'\n", cell_name);
+ return PTR_ERR(efuse);
+ }
+
+ lvts_td->calib = devm_krealloc(dev, lvts_td->calib,
+ lvts_td->calib_len + len, GFP_KERNEL);
+ if (!lvts_td->calib)
+ return -ENOMEM;
+
+ memcpy(lvts_td->calib + lvts_td->calib_len, efuse, len);
+
+ lvts_td->calib_len += len;
+
+ kfree(efuse);
+ }
+
+ return 0;
+}
+
+static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
+{
+ u32 gt;
+
+ gt = (*value) >> 24;
+
+ if (gt && gt < LVTS_GOLDEN_TEMP_MAX)
+ golden_temp = gt;
+
+ coeff_b = golden_temp * 500 + LVTS_COEFF_B;
+
+ return 0;
+}
+
+static int __init lvts_ctrl_init(struct device *dev,
+ struct lvts_domain *lvts_td,
+ struct lvts_data *lvts_data)
+{
+ size_t size = sizeof(*lvts_td->lvts_ctrl) * lvts_data->num_lvts_ctrl;
+ struct lvts_ctrl *lvts_ctrl;
+ int i, ret;
+
+ /*
+ * Create the calibration bytes stream from efuse data
+ */
+ ret = lvts_calibration_read(dev, lvts_td, lvts_data);
+ if (ret)
+ return ret;
+
+ /*
+ * The golden temp information is contained in the first chunk
+ * of efuse data.
+ */
+ ret = lvts_golden_temp_init(dev, (u32 *)lvts_td->calib);
+ if (ret)
+ return ret;
+
+ lvts_ctrl = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!lvts_ctrl)
+ return -ENOMEM;
+
+ for (i = 0; i < lvts_data->num_lvts_ctrl; i++) {
+
+ lvts_ctrl[i].base = lvts_td->base + lvts_data->lvts_ctrl[i].offset;
+
+ ret = lvts_sensor_init(dev, &lvts_ctrl[i],
+ &lvts_data->lvts_ctrl[i]);
+ if (ret)
+ return ret;
+
+ ret = lvts_calibration_init(dev, &lvts_ctrl[i],
+ &lvts_data->lvts_ctrl[i],
+ lvts_td->calib);
+ if (ret)
+ return ret;
+
+ /*
+ * The mode the ctrl will use to read the temperature
+ * (filtered or immediate)
+ */
+ lvts_ctrl[i].mode = lvts_data->lvts_ctrl[i].mode;
+
+ /*
+ * The temperature to raw temperature must be done
+ * after initializing the calibration.
+ */
+ lvts_ctrl[i].hw_tshut_raw_temp =
+ lvts_temp_to_raw(lvts_data->lvts_ctrl[i].hw_tshut_temp);
+ }
+
+ /*
+ * We no longer need the efuse bytes stream, let's free it
+ */
+ devm_kfree(dev, lvts_td->calib);
+
+ lvts_td->lvts_ctrl = lvts_ctrl;
+ lvts_td->num_lvts_ctrl = lvts_data->num_lvts_ctrl;
+
+ return 0;
+}
+
+/*
+ * At this point the configuration register is the only place in the
+ * driver where we write multiple values. Per hardware constraint,
+ * each write in the configuration register must be separated by a
+ * delay of 2 us.
+ */
+static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, u32 *cmds, int nr_cmds)
+{
+ int i;
+
+ /*
+ * Configuration register
+ */
+ for (i = 0; i < nr_cmds; i++) {
+ writel(cmds[i], LVTS_CONFIG(lvts_ctrl->base));
+ usleep_range(2, 4);
+ }
+}
+
+static int lvts_irq_init(struct lvts_ctrl *lvts_ctrl)
+{
+ /*
+ * LVTS_PROTCTL : Thermal Protection Sensor Selection
+ *
+ * Bits:
+ *
+ * 19-18 : Sensor to base the protection on
+ * 17-16 : Strategy:
+ * 00 : Average of 4 sensors
+ * 01 : Max of 4 sensors
+ * 10 : Selected sensor with bits 19-18
+ * 11 : Reserved
+ */
+ writel(BIT(16), LVTS_PROTCTL(lvts_ctrl->base));
+
+ /*
+ * LVTS_PROTTA : Stage 1 temperature threshold
+ * LVTS_PROTTB : Stage 2 temperature threshold
+ * LVTS_PROTTC : Stage 3 temperature threshold
+ *
+ * Bits:
+ *
+ * 14-0: Raw temperature threshold
+ *
+ * writel(0x0, LVTS_PROTTA(lvts_ctrl->base));
+ * writel(0x0, LVTS_PROTTB(lvts_ctrl->base));
+ */
+ writel(lvts_ctrl->hw_tshut_raw_temp, LVTS_PROTTC(lvts_ctrl->base));
+
+ /*
+ * LVTS_MONINT : Interrupt configuration register
+ *
+ * The LVTS_MONINT register layout is the same as the LVTS_MONINTSTS
+ * register, except we set the bits to enable the interrupt.
+ */
+ writel(LVTS_MONINT_CONF, LVTS_MONINT(lvts_ctrl->base));
+
+ return 0;
+}
+
+static int lvts_domain_reset(struct device *dev, struct reset_control *reset)
+{
+ int ret;
+
+ ret = reset_control_assert(reset);
+ if (ret)
+ return ret;
+
+ return reset_control_deassert(reset);
+}
+
+/*
+ * Enable or disable the clocks of a specified thermal controller
+ */
+static int lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable)
+{
+ /*
+ * LVTS_CLKEN : Internal LVTS clock
+ *
+ * Bits:
+ *
+ * 0 : enable / disable clock
+ */
+ writel(enable, LVTS_CLKEN(lvts_ctrl->base));
+
+ return 0;
+}
+
+static inline int lvts_ctrl_enable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ return lvts_ctrl_set_enable(lvts_ctrl, 1);
+}
+
+static inline int lvts_ctrl_disable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ return lvts_ctrl_set_enable(lvts_ctrl, 0);
+}
+
+static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 };
+
+ lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
+
+ /*
+ * LVTS_ID : Get ID and status of the thermal controller
+ *
+ * Bits:
+ *
+ * 0-5 : thermal controller id
+ * 7 : thermal controller connection is valid
+ */
+ id = readl(LVTS_ID(lvts_ctrl->base));
+ if (!(id & BIT(7)))
+ return -EIO;
+
+ return 0;
+}
+
+static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ /*
+ * Write device mask: 0xC1030000
+ */
+ u32 cmds[] = {
+ 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
+ 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
+ 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
+ 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
+ };
+
+ lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
+
+ return 0;
+}
+
+static int lvts_ctrl_calibrate(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ int i;
+ void __iomem *lvts_edata[] = {
+ LVTS_EDATA00(lvts_ctrl->base),
+ LVTS_EDATA01(lvts_ctrl->base),
+ LVTS_EDATA02(lvts_ctrl->base),
+ LVTS_EDATA03(lvts_ctrl->base)
+ };
+
+ /*
+ * LVTS_EDATA0X : Efuse calibration reference value for sensor X
+ *
+ * Bits:
+ *
+ * 20-0 : Efuse value for normalization data
+ */
+ for (i = 0; i < LVTS_SENSOR_MAX; i++)
+ writel(lvts_ctrl->calibration[i], lvts_edata[i]);
+
+ return 0;
+}
+
+static int lvts_ctrl_configure(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ u32 value;
+
+ /*
+ * LVTS_TSSEL : Sensing point index numbering
+ *
+ * Bits:
+ *
+ * 31-24: ADC Sense 3
+ * 23-16: ADC Sense 2
+ * 15-8 : ADC Sense 1
+ * 7-0 : ADC Sense 0
+ */
+ value = LVTS_TSSEL_CONF;
+ writel(value, LVTS_TSSEL(lvts_ctrl->base));
+
+ /*
+ * LVTS_CALSCALE : ADC voltage round
+ */
+ value = 0x300;
+ value = LVTS_CALSCALE_CONF;
+
+ /*
+ * LVTS_MSRCTL0 : Sensor filtering strategy
+ *
+ * Filters:
+ *
+ * 000 : One sample
+ * 001 : Avg 2 samples
+ * 010 : 4 samples, drop min and max, avg 2 samples
+ * 011 : 6 samples, drop min and max, avg 4 samples
+ * 100 : 10 samples, drop min and max, avg 8 samples
+ * 101 : 18 samples, drop min and max, avg 16 samples
+ *
+ * Bits:
+ *
+ * 0-2 : Sensor0 filter
+ * 3-5 : Sensor1 filter
+ * 6-8 : Sensor2 filter
+ * 9-11 : Sensor3 filter
+ */
+ value = LVTS_HW_FILTER << 9 | LVTS_HW_FILTER << 6 |
+ LVTS_HW_FILTER << 3 | LVTS_HW_FILTER;
+ writel(value, LVTS_MSRCTL0(lvts_ctrl->base));
+
+ /*
+ * LVTS_MSRCTL1 : Measurement control
+ *
+ * Bits:
+ *
+ * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3
+ * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2
+ * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1
+ * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0
+ *
+ * That configuration will ignore the filtering and the delays
+ * introduced below in MONCTL1 and MONCTL2
+ */
+ if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) {
+ value = BIT(9) | BIT(6) | BIT(5) | BIT(4);
+ writel(value, LVTS_MSRCTL1(lvts_ctrl->base));
+ }
+
+ /*
+ * LVTS_MONCTL1 : Period unit and group interval configuration
+ *
+ * The clock source of LVTS thermal controller is 26MHz.
+ *
+ * The period unit is a time base for all the interval delays
+ * specified in the registers. By default we use 12. The time
+ * conversion is done by multiplying by 256 and 1/26.10^6
+ *
+ * An interval delay multiplied by the period unit gives the
+ * duration in seconds.
+ *
+ * - Filter interval delay is a delay between two samples of
+ * the same sensor.
+ *
+ * - Sensor interval delay is a delay between two samples of
+ * different sensors.
+ *
+ * - Group interval delay is a delay between different rounds.
+ *
+ * For example:
+ * If Period unit = C, filter delay = 1, sensor delay = 2, group delay = 1,
+ * and two sensors, TS1 and TS2, are in a LVTS thermal controller
+ * and then
+ * Period unit time = C * 1/26M * 256 = 12 * 38.46ns * 256 = 118.149us
+ * Filter interval delay = 1 * Period unit = 118.149us
+ * Sensor interval delay = 2 * Period unit = 236.298us
+ * Group interval delay = 1 * Period unit = 118.149us
+ *
+ * TS1 TS1 ... TS1 TS2 TS2 ... TS2 TS1...
+ * <--> Filter interval delay
+ * <--> Sensor interval delay
+ * <--> Group interval delay
+ * Bits:
+ * 29 - 20 : Group interval
+ * 16 - 13 : Send a single interrupt when crossing the hot threshold (1)
+ * or an interrupt everytime the hot threshold is crossed (0)
+ * 9 - 0 : Period unit
+ *
+ */
+ value = LVTS_GROUP_INTERVAL << 20 | LVTS_PERIOD_UNIT;
+ writel(value, LVTS_MONCTL1(lvts_ctrl->base));
+
+ /*
+ * LVTS_MONCTL2 : Filtering and sensor interval
+ *
+ * Bits:
+ *
+ * 25-16 : Interval unit in PERIOD_UNIT between sample on
+ * the same sensor, filter interval
+ * 9-0 : Interval unit in PERIOD_UNIT between each sensor
+ *
+ */
+ value = LVTS_FILTER_INTERVAL << 16 | LVTS_SENSOR_INTERVAL;
+ writel(value, LVTS_MONCTL2(lvts_ctrl->base));
+
+ return lvts_irq_init(lvts_ctrl);
+}
+
+static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl)
+{
+ struct lvts_sensor *lvts_sensors = lvts_ctrl->sensors;
+ struct thermal_zone_device *tz;
+ u32 sensor_map = 0;
+ int i;
+
+ for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) {
+
+ int dt_id = lvts_sensors[i].dt_id;
+
+ tz = devm_thermal_of_zone_register(dev, dt_id, &lvts_sensors[i],
+ &lvts_ops);
+ if (IS_ERR(tz)) {
+ /*
+ * This thermal zone is not described in the
+ * device tree. It is not an error from the
+ * thermal OF code POV, we just continue.
+ */
+ if (PTR_ERR(tz) == -ENODEV)
+ continue;
+
+ return PTR_ERR(tz);
+ }
+
+ /*
+ * The thermal zone pointer will be needed in the
+ * interrupt handler, we store it in the sensor
+ * structure. The thermal domain structure will be
+ * passed to the interrupt handler private data as the
+ * interrupt is shared for all the controller
+ * belonging to the thermal domain.
+ */
+ lvts_sensors[i].tz = tz;
+
+ /*
+ * This sensor was correctly associated with a thermal
+ * zone, let's set the corresponding bit in the sensor
+ * map, so we can enable the temperature monitoring in
+ * the hardware thermal controller.
+ */
+ sensor_map |= BIT(i);
+ }
+
+ /*
+ * Bits:
+ * 9: Single point access flow
+ * 0-3: Enable sensing point 0-3
+ *
+ * The initialization of the thermal zones give us
+ * which sensor point to enable. If any thermal zone
+ * was not described in the device tree, it won't be
+ * enabled here in the sensor map.
+ */
+ writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
+
+ return 0;
+}
+
+static int lvts_domain_init(struct device *dev, struct lvts_domain *lvts_td,
+ struct lvts_data *lvts_data)
+{
+ struct lvts_ctrl *lvts_ctrl;
+ int i, ret;
+
+ ret = lvts_ctrl_init(dev, lvts_td, lvts_data);
+ if (ret)
+ return ret;
+
+ ret = lvts_domain_reset(dev, lvts_td->reset);
+ if (ret) {
+ dev_dbg(dev, "Failed to reset domain");
+ return ret;
+ }
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+
+ lvts_ctrl = &lvts_td->lvts_ctrl[i];
+
+ /*
+ * Initialization steps:
+ *
+ * - Enable the clock
+ * - Connect to the LVTS
+ * - Initialize the LVTS
+ * - Prepare the calibration data
+ * - Select monitored sensors
+ * [ Configure sampling ]
+ * [ Configure the interrupt ]
+ * - Start measurement
+ */
+ ret = lvts_ctrl_enable(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to enable LVTS clock");
+ return ret;
+ }
+
+ ret = lvts_ctrl_connect(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to connect to LVTS controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_initialize(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to initialize controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_calibrate(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to calibrate controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_configure(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to configure controller");
+ return ret;
+ }
+
+ ret = lvts_ctrl_start(dev, lvts_ctrl);
+ if (ret) {
+ dev_dbg(dev, "Failed to start controller");
+ return ret;
+ }
+ }
+
+ return lvts_debugfs_init(dev, lvts_td);
+}
+
+static int lvts_probe(struct platform_device *pdev)
+{
+ struct lvts_data *lvts_data;
+ struct lvts_domain *lvts_td;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int irq, ret;
+
+ lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL);
+ if (!lvts_td)
+ return -ENOMEM;
+
+ lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
+ if (!lvts_data) {
+ dev_dbg(dev, "No platforme data");
+ return -ENODATA;
+ };
+
+ lvts_td->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(lvts_td->clk)) {
+ dev_dbg(dev, "Failed to retrieve clock\n");
+ return PTR_ERR(lvts_td->clk);
+ }
+
+ res = platform_get_mem_or_io(pdev, 0);
+ if (!res) {
+ dev_dbg(dev, "No IO resource\n");
+ return -ENXIO;
+ }
+
+ lvts_td->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lvts_td->base)) {
+ dev_dbg(dev, "Failed to map io resource\n");
+ return PTR_ERR(lvts_td->base);
+ }
+
+ lvts_td->reset = devm_reset_control_get_by_index(dev, 0);
+ if (IS_ERR(lvts_td->reset)) {
+ dev_dbg(dev, "Failed to get reset control\n");
+ return PTR_ERR(lvts_td->reset);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_dbg(dev, "No irq resource\n");
+ return irq;
+ }
+
+ ret = lvts_domain_init(dev, lvts_td, lvts_data);
+ if (ret) {
+ dev_dbg(dev, "Failed to initialize the lvts domain\n");
+ return ret;
+ }
+
+ /*
+ * At this point the LVTS is initialized and enabled. We can
+ * safely enable the interrupt.
+ */
+ ret = devm_request_threaded_irq(dev, irq, NULL, lvts_irq_handler,
+ IRQF_ONESHOT, dev_name(dev), lvts_td);
+ if (ret) {
+ dev_dbg(dev, "Failed to request interrupt\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lvts_td);
+
+ return 0;
+}
+
+static int lvts_remove(struct platform_device *pdev)
+{
+ struct lvts_domain *lvts_td;
+ struct device *dev = &pdev->dev;
+ int i;
+
+ lvts_td = platform_get_drvdata(pdev);
+
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
+ lvts_ctrl_disable(dev, &lvts_td->lvts_ctrl[i]);
+
+ lvts_debugfs_exit();
+
+ return 0;
+}
+
+static struct lvts_ctrl_data mt8195_lvts_data_ctrl[] = {
+ {
+ .cal_offset = { 0x4, 0x7 },
+ .lvts_sensor = {
+ { .dt_id = MT8195_MCU_BIG_CPU0 },
+ { .dt_id = MT8195_MCU_BIG_CPU1 }
+ },
+ .num_lvts_sensor = 2,
+ .offset = 0x0,
+ .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
+ },
+
+ {
+ .cal_offset = { 0xd, 0x10 },
+ .lvts_sensor = {
+ { .dt_id = MT8195_MCU_BIG_CPU2 },
+ { .dt_id = MT8195_MCU_BIG_CPU3 }
+ },
+ .num_lvts_sensor = 2,
+ .offset = 0x100,
+ .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
+ },
+
+ {
+ .cal_offset = { 0x16, 0x19, 0x1c, 0x1f },
+ .lvts_sensor = {
+ { .dt_id = MT8195_MCU_LITTLE_CPU0 },
+ { .dt_id = MT8195_MCU_LITTLE_CPU1 },
+ { .dt_id = MT8195_MCU_LITTLE_CPU2 },
+ { .dt_id = MT8195_MCU_LITTLE_CPU3 }
+ },
+ .num_lvts_sensor = 4,
+ .offset = 0x200,
+ .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
+ }
+};
+
+static struct lvts_data mt8195_lvts_mcu_data = {
+ .lvts_ctrl = mt8195_lvts_data_ctrl,
+ .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_data_ctrl),
+};
+
+static const struct of_device_id lvts_of_match[] = {
+ { .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lvts_of_match);
+
+static struct platform_driver lvts_driver = {
+ .probe = lvts_probe,
+ .remove = lvts_remove,
+ .driver = {
+ .name = "mtk-lvts-thermal",
+ .of_match_table = lvts_of_match,
+ },
+};
+module_platform_driver(lvts_driver);
+
+MODULE_AUTHOR("Balsam CHIHI <[email protected]>");
+MODULE_DESCRIPTION("MediaTek LVTS Thermal Driver");
+MODULE_LICENSE("GPL");
--
2.34.1
On 30/01/2023 11:40, Balsam CHIHI wrote:
>>> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
>>> new file mode 100644
>>> index 000000000000..902d5b1e4f43
>>> --- /dev/null
>>> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
>>
>> Same filename as bindings.
>
> fixed.
> rename :
> include/dt-bindings/thermal/mediatek-lvts.h =>
> include/dt-bindings/thermal/mediatek-lvts-thermal.h
Missing coma, so mediatek,lvts-thermal.h
Best regards,
Krzysztof
On 31/01/2023 17:53, Krzysztof Kozlowski wrote:
> On 30/01/2023 11:40, Balsam CHIHI wrote:
>>>> diff --git a/include/dt-bindings/thermal/mediatek-lvts.h b/include/dt-bindings/thermal/mediatek-lvts.h
>>>> new file mode 100644
>>>> index 000000000000..902d5b1e4f43
>>>> --- /dev/null
>>>> +++ b/include/dt-bindings/thermal/mediatek-lvts.h
>>>
>>> Same filename as bindings.
>>
>> fixed.
>> rename :
>> include/dt-bindings/thermal/mediatek-lvts.h =>
>> include/dt-bindings/thermal/mediatek-lvts-thermal.h
>
> Missing coma, so mediatek,lvts-thermal.h
Yeah, actually Balsam resent a new version but numbered v3 taking into
account your comments.
The versioning is becoming a bit messy now but if you are ok with the
changes I'll pick the patch as is so we can go forward for this series.
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
Hi Balsam,
Thank you for the patch! Yet something to improve:
url: https://github.com/intel-lab-lkp/linux/commits/UPDATE-20230131-234122/bchihi-baylibre-com/thermal-drivers-mediatek-Relocate-driver-to-mediatek-folder/20230124-211910
base: the 4th patch of https://lore.kernel.org/r/20230124131717.128660-5-bchihi%40baylibre.com
patch link: https://lore.kernel.org/r/20230131153816.21709-1-bchihi%40baylibre.com
patch subject: [PATCH v12] thermal: drivers: mediatek: Add the Low Voltage Thermal Sensor driver
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20230201/[email protected]/config)
compiler: s390-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/5646ebf6f10ff0fc60c04d8c57523f7c44526b41
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review UPDATE-20230131-234122/bchihi-baylibre-com/thermal-drivers-mediatek-Relocate-driver-to-mediatek-folder/20230124-211910
git checkout 5646ebf6f10ff0fc60c04d8c57523f7c44526b41
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
>> drivers/thermal/mediatek/lvts_thermal.c:20:10: fatal error: dt-bindings/thermal/mediatek,lvts-thermal.h: No such file or directory
20 | #include <dt-bindings/thermal/mediatek,lvts-thermal.h>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
vim +20 drivers/thermal/mediatek/lvts_thermal.c
> 20 #include <dt-bindings/thermal/mediatek,lvts-thermal.h>
21
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
On 31/01/2023 15:04, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> Add LVTS thermal controllers dt-binding definition for mt8195.
>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> Changelog:
> v3:
> - Fixed subject prefix
> - Fixed licenses GPL-2.0-only OR BSD-2-Clause
> to GPL-2.0 OR MIT (to match DT)
> - Fixed matching dt-binding file names
If this is v3 with only one patch, where is the driver or DTS using
these bindings? Please link it. It's unusual to see only bindings,
without the users.
> v2:
> - Fixed subject prefix
> - Fixed licenses GPL-2.0+ to GPL-2.0
> - Added dual licenses
Is there a reason to make our review more difficult and keep versions
broken, threads attached to some other threads?
------------
Grabbing thread from
lore.kernel.org/all/20230131140439.600164-1-bchihi%40baylibre.com/t.mbox.gz
Checking for newer revisions on https://lore.kernel.org/all/
Analyzing 38 messages in the thread
Will use the latest revision: v12
You can pick other revisions using the -vN flag
Checking attestation on all messages, may take a moment...
---
✓ [PATCH v12] thermal: drivers: mediatek: Add the Low Voltage Thermal
Sensor driver
✓ Signed: DKIM/baylibre-com.20210112.gappssmtp.com (From:
[email protected])
+ Link:
https://lore.kernel.org/r/[email protected]
✓ [PATCH v12 2/6] dt-bindings: thermal: mediatek: Add LVTS thermal
controllers dt-binding definition
✓ Signed: DKIM/baylibre-com.20210112.gappssmtp.com (From:
[email protected])
+ Link:
https://lore.kernel.org/r/[email protected]
ERROR: missing [3/1]!
ERROR: missing [4/1]!
ERROR: missing [5/1]!
ERROR: missing [6/1]!
--------
b4 diff '<[email protected]>'
Checking for older revisions on https://lore.kernel.org/all/
---
Analyzing 38 messages in the thread
Assuming new revision: v4 ([PATCH v12] thermal: drivers: mediatek: Add
the Low Voltage Thermal Sensor driver)
Preparing fake-am for v3: dt-bindings: thermal: mediatek: Add LVTS
thermal controllers
range: 291580cde5f6..de7fe5e0293a
Preparing fake-am for v12: arm64: dts: mediatek: mt8195: Add thermal
zones and thermal nodes
ERROR: Could not find matching blob for
arch/arm64/boot/dts/mediatek/mt8195.dtsi (09df105f4606)
If you know on which tree this patchset is based,
add it as a remote and perform "git remote update"
in order to fetch the missing objects.
---
Could not create fake-am range for upper series v12
> ---
> ---
> .../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
> .../thermal/mediatek,lvts-thermal.h | 19 ++++
> 2 files changed, 126 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> create mode 100644 include/dt-bindings/thermal/mediatek,lvts-thermal.h
>
> diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> new file mode 100644
> index 000000000000..5fa5c7a1a417
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> @@ -0,0 +1,107 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR MIT)
WARNING: DT binding documents should be licensed (GPL-2.0-only OR
BSD-2-Clause)
#24: FILE:
Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml:1:
+# SPDX-License-Identifier: (GPL-2.0 OR MIT)
I asked you to use the binding license for header file. Then you changed
binding license... why? Why do you need other SPDX text? Why do you need
MIT?
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
> +
> +maintainers:
> + - Balsam CHIHI <[email protected]>
> +
> +description: |
> + LVTS is a thermal management architecture composed of three subsystems,
> + a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
> + a Converter - Low Voltage Thermal Sensor converter (LVTS), and
> + a Digital controller (LVTS_CTRL).
> +
> +properties:
> + compatible:
> + enum:
> + - mediatek,mt8195-lvts-ap
> + - mediatek,mt8195-lvts-mcu
What about other devices? You called the file name as generic for all
Mediatek SoCs, so why only one SoC is here? Is there going to be more?
If yes, why they cannot be added now?
Best regards,
Krzysztof
On 01/02/2023 04:09, kernel test robot wrote:
> Hi Balsam,
>
> Thank you for the patch! Yet something to improve:
>
>
>
> url: https://github.com/intel-lab-lkp/linux/commits/UPDATE-20230131-234122/bchihi-baylibre-com/thermal-drivers-mediatek-Relocate-driver-to-mediatek-folder/20230124-211910
> base: the 4th patch of https://lore.kernel.org/r/20230124131717.128660-5-bchihi%40baylibre.com
> patch link: https://lore.kernel.org/r/20230131153816.21709-1-bchihi%40baylibre.com
> patch subject: [PATCH v12] thermal: drivers: mediatek: Add the Low Voltage Thermal Sensor driver
> config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20230201/[email protected]/config)
> compiler: s390-linux-gcc (GCC) 12.1.0
> reproduce (this is a W=1 build):
> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> # https://github.com/intel-lab-lkp/linux/commit/5646ebf6f10ff0fc60c04d8c57523f7c44526b41
> git remote add linux-review https://github.com/intel-lab-lkp/linux
> git fetch --no-tags linux-review UPDATE-20230131-234122/bchihi-baylibre-com/thermal-drivers-mediatek-Relocate-driver-to-mediatek-folder/20230124-211910
> git checkout 5646ebf6f10ff0fc60c04d8c57523f7c44526b41
> # save the config file
> mkdir build_dir && cp config build_dir/.config
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 olddefconfig
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash
>
> If you fix the issue, kindly add following tag where applicable
> | Reported-by: kernel test robot <[email protected]>
>
> All errors (new ones prefixed by >>):
>
>>> drivers/thermal/mediatek/lvts_thermal.c:20:10: fatal error: dt-bindings/thermal/mediatek,lvts-thermal.h: No such file or directory
> 20 | #include <dt-bindings/thermal/mediatek,lvts-thermal.h>
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So here is your driver... and you decided not only to make reviewers
life more difficult, but also to fail all automated tools.
No, that's not how patchsets should be sent. You have here clear
dependency, you cannot send them separately.
Best regards,
Krzysztof
On 31/01/2023 16:38, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
> controllers contained in a thermal domain.
>
> A thermal domains can be the MCU or the AP.
>
> Each thermal domains contain up to seven controllers, each thermal
> controller handle up to four thermal sensors.
>
> The LVTS has two Finite State Machines (FSM), one to handle the
> functionin temperatures range like hot or cold temperature and another
> one to handle monitoring trip point. The FSM notifies via interrupts
> when a trip point is crossed.
>
(...)
> +
> +struct lvts_domain {
> + struct lvts_ctrl *lvts_ctrl;
> + struct reset_control *reset;
> + struct clk *clk;
> + int num_lvts_ctrl;
> + void __iomem *base;
> + size_t calib_len;
> + u8 *calib;
> +};
> +
> +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
> +
> +static struct dentry *root;
How do you handle two instances of driver?
> +
> +#define LVTS_DEBUG_FS_REGS(__reg) \
> +{ \
> + .name = __stringify(__reg), \
> + .offset = __reg(0), \
> +}
> +
(...)
> +
> +static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high)
> +{
> + struct lvts_sensor *lvts_sensor = tz->devdata;
> + void __iomem *base = lvts_sensor->base;
> + u32 raw_low = lvts_temp_to_raw(low);
> + u32 raw_high = lvts_temp_to_raw(high);
> +
> + /*
> + * Hot to normal temperature threshold
> + *
> + * LVTS_H2NTHRE
> + *
> + * Bits:
> + *
> + * 14-0 : Raw temperature for threshold
> + */
> + if (low != -INT_MAX) {
> + dev_dbg(&tz->device, "Setting low limit temperature interrupt: %d\n", low);
> + writel(raw_low, LVTS_H2NTHRE(base));
> + }
> +
> + /*
> + * Hot temperature threshold
> + *
> + * LVTS_HTHRE
> + *
> + * Bits:
> + *
> + * 14-0 : Raw temperature for threshold
> + */
> + dev_dbg(&tz->device, "Setting high limit temperature interrupt: %d\n", high);
> + writel(raw_high, LVTS_HTHRE(base));
> +
> + return 0;
> +}
> +
> +static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl)
> +{
> + irqreturn_t iret = IRQ_NONE;
> + u32 value, masks[] = {
Don't mix different types in one declaration. u32 and a pointer are
quite different types.
> + LVTS_INT_SENSOR0,
> + LVTS_INT_SENSOR1,
> + LVTS_INT_SENSOR2,
> + LVTS_INT_SENSOR3
> + };
> + int i;
> +
> + /*
> + * Interrupt monitoring status
> + *
> + * LVTS_MONINTST
> + *
> + * Bits:
> + *
> + * 31 : Interrupt for stage 3
> + * 30 : Interrupt for stage 2
> + * 29 : Interrupt for state 1
> + * 28 : Interrupt using filter on sensor 3
> + *
> + * 27 : Interrupt using immediate on sensor 3
> + * 26 : Interrupt normal to hot on sensor 3
> + * 25 : Interrupt high offset on sensor 3
> + * 24 : Interrupt low offset on sensor 3
> + *
> + * 23 : Interrupt hot threshold on sensor 3
> + * 22 : Interrupt cold threshold on sensor 3
> + * 21 : Interrupt using filter on sensor 2
> + * 20 : Interrupt using filter on sensor 1
> + *
> + * 19 : Interrupt using filter on sensor 0
> + * 18 : Interrupt using immediate on sensor 2
> + * 17 : Interrupt using immediate on sensor 1
> + * 16 : Interrupt using immediate on sensor 0
> + *
> + * 15 : Interrupt device access timeout interrupt
> + * 14 : Interrupt normal to hot on sensor 2
> + * 13 : Interrupt high offset interrupt on sensor 2
> + * 12 : Interrupt low offset interrupt on sensor 2
> + *
> + * 11 : Interrupt hot threshold on sensor 2
> + * 10 : Interrupt cold threshold on sensor 2
> + * 9 : Interrupt normal to hot on sensor 1
> + * 8 : Interrupt high offset interrupt on sensor 1
> + *
> + * 7 : Interrupt low offset interrupt on sensor 1
> + * 6 : Interrupt hot threshold on sensor 1
> + * 5 : Interrupt cold threshold on sensor 1
> + * 4 : Interrupt normal to hot on sensor 0
> + *
> + * 3 : Interrupt high offset interrupt on sensor 0
> + * 2 : Interrupt low offset interrupt on sensor 0
> + * 1 : Interrupt hot threshold on sensor 0
> + * 0 : Interrupt cold threshold on sensor 0
> + *
> + * We are interested in the sensor(s) responsible of the
> + * interrupt event. We update the thermal framework with the
> + * thermal zone associated with the sensor. The framework will
> + * take care of the rest whatever the kind of interrupt, we
> + * are only interested in which sensor raised the interrupt.
> + *
> + * sensor 3 interrupt: 0001 1111 1100 0000 0000 0000 0000 0000
> + * => 0x1FC00000
> + * sensor 2 interrupt: 0000 0000 0010 0100 0111 1100 0000 0000
> + * => 0x00247C00
> + * sensor 1 interrupt: 0000 0000 0001 0001 0000 0011 1110 0000
> + * => 0X000881F0
> + * sensor 0 interrupt: 0000 0000 0000 1001 0000 0000 0001 1111
> + * => 0x0009001F
> + */
> + value = readl(LVTS_MONINTSTS(lvts_ctrl->base));
> +
> + /*
> + * Let's figure out which sensors raised the interrupt
> + *
> + * NOTE: the masks array must be ordered with the index
> + * corresponding to the sensor id eg. index=0, mask for
> + * sensor0.
> + */
> + for (i = 0; i < ARRAY_SIZE(masks); i++) {
> +
> + if (!(value & masks[i]))
> + continue;
> +
> + thermal_zone_device_update(lvts_ctrl->sensors[i].tz,
> + THERMAL_TRIP_VIOLATED);
> + iret = IRQ_HANDLED;
> + }
> +
> + /*
> + * Write back to clear the interrupt status (W1C)
> + */
> + writel(value, LVTS_MONINTSTS(lvts_ctrl->base));
> +
> + return iret;
> +}
> +
> +/*
> + * Temperature interrupt handler. Even if the driver supports more
> + * interrupt modes, we use the interrupt when the temperature crosses
> + * the hot threshold the way up and the way down (modulo the
> + * hysteresis).
> + *
> + * Each thermal domain has a couple of interrupts, one for hardware
> + * reset and another one for all the thermal events happening on the
> + * different sensors.
> + *
> + * The interrupt is configured for thermal events when crossing the
> + * hot temperature limit. At each interrupt, we check in every
> + * controller if there is an interrupt pending.
> + */
> +static irqreturn_t lvts_irq_handler(int irq, void *data)
> +{
> + struct lvts_domain *lvts_td = data;
> + irqreturn_t aux, iret = IRQ_NONE;
> + int i;
> +
> + for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
> +
> + aux = lvts_ctrl_irq_handler(lvts_td->lvts_ctrl);
> + if (aux != IRQ_HANDLED)
> + continue;
> +
> + iret = IRQ_HANDLED;
> + }
> +
> + return iret;
> +}
> +
> +static struct thermal_zone_device_ops lvts_ops = {
> + .get_temp = lvts_get_temp,
> + .set_trips = lvts_set_trips,
> +};
> +
> +static int __init lvts_sensor_init(struct device *dev,
> + struct lvts_ctrl *lvts_ctrl,
> + struct lvts_ctrl_data *lvts_ctrl_data)
> +{
> + struct lvts_sensor *lvts_sensor = lvts_ctrl->sensors;
> + void __iomem *msr_regs[] = {
> + LVTS_MSR0(lvts_ctrl->base),
> + LVTS_MSR1(lvts_ctrl->base),
> + LVTS_MSR2(lvts_ctrl->base),
> + LVTS_MSR3(lvts_ctrl->base)
> + };
> +
> + void __iomem *imm_regs[] = {
> + LVTS_IMMD0(lvts_ctrl->base),
> + LVTS_IMMD1(lvts_ctrl->base),
> + LVTS_IMMD2(lvts_ctrl->base),
> + LVTS_IMMD3(lvts_ctrl->base)
> + };
> +
> + int i;
> +
> + for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) {
> +
> + int dt_id = lvts_ctrl_data->lvts_sensor[i].dt_id;
> +
> + /*
> + * At this point, we don't know which id matches which
> + * sensor. Let's set arbitrally the id from the index.
> + */
> + lvts_sensor[i].id = i;
> +
> + /*
> + * The thermal zone registration will set the trip
> + * point interrupt in the thermal controller
> + * register. But this one will be reset in the
> + * initialization after. So we need to post pone the
> + * thermal zone creation after the controller is
> + * setup. For this reason, we store the device tree
> + * node id from the data in the sensor structure
> + */
> + lvts_sensor[i].dt_id = dt_id;
> +
> + /*
> + * We assign the base address of the thermal
> + * controller as a back pointer. So it will be
> + * accessible from the different thermal framework ops
> + * as we pass the lvts_sensor pointer as thermal zone
> + * private data.
> + */
> + lvts_sensor[i].base = lvts_ctrl->base;
> +
> + /*
> + * Each sensor has its own register address to read from.
> + */
> + lvts_sensor[i].msr = lvts_ctrl_data->mode == LVTS_MSR_IMMEDIATE_MODE ?
> + imm_regs[i] : msr_regs[i];
> + };
> +
> + lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor;
> +
> + return 0;
> +}
> +
> +/*
> + * The efuse blob values follows the sensor enumeration per thermal
> + * controller. The decoding of the stream is as follow:
> + *
> + * <--?-> <----big0 ???---> <-sensor0-> <-0->
> + * ------------------------------------------
> + * index in the stream: : | 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 |
> + * ------------------------------------------
> + *
> + * <--sensor1--><-0-> <----big1 ???---> <-sen
> + * ------------------------------------------
> + * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
> + * ------------------------------------------
> + *
> + * sor0-> <-0-> <-sensor1-> <-0-> ..........
> + * ------------------------------------------
> + * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
> + * ------------------------------------------
> + *
> + * And so on ...
> + *
> + * The data description gives the offset of the calibration data in
> + * this bytes stream for each sensor.
> + *
> + * Each thermal controller can handle up to 4 sensors max, we don't
> + * care if there are less as the array of calibration is sized to 4
> + * anyway. The unused sensor slot will be zeroed.
> + */
> +static int __init lvts_calibration_init(struct device *dev,
> + struct lvts_ctrl *lvts_ctrl,
> + struct lvts_ctrl_data *lvts_ctrl_data,
> + u8 *efuse_calibration)
> +{
> + int i;
> +
> + for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++)
> + memcpy(&lvts_ctrl->calibration[i],
> + efuse_calibration + lvts_ctrl_data->cal_offset[i], 2);
> +
> + return 0;
> +}
> +
> +/*
> + * The efuse bytes stream can be split into different chunk of
> + * nvmems. This function reads and concatenate those into a single
> + * buffer so it can be read sequentially when initializing the
> + * calibration data.
> + */
> +static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td,
> + struct lvts_data *lvts_data)
> +{
> + struct device_node *np = dev_of_node(dev);
> + struct nvmem_cell *cell;
> + struct property *prop;
> + const char *cell_name;
> +
> + of_property_for_each_string(np, "nvmem-cell-names", prop, cell_name) {
> + size_t len;
> + u8 *efuse;
> +
> + cell = of_nvmem_cell_get(np, cell_name);
> + if (IS_ERR(cell)) {
> + dev_dbg(dev, "Failed to get cell '%s'\n", cell_name);
Is this an error? If so, why debug? dbg is not for errors.
> + return PTR_ERR(cell);
> + }
> +
> + efuse = nvmem_cell_read(cell, &len);
> +
> + nvmem_cell_put(cell);
> +
> + if (IS_ERR(efuse)) {
> + dev_dbg(dev, "Failed to read cell '%s'\n", cell_name);
> + return PTR_ERR(efuse);
> + }
> +
> + lvts_td->calib = devm_krealloc(dev, lvts_td->calib,
> + lvts_td->calib_len + len, GFP_KERNEL);
> + if (!lvts_td->calib)
> + return -ENOMEM;
> +
> + memcpy(lvts_td->calib + lvts_td->calib_len, efuse, len);
> +
> + lvts_td->calib_len += len;
> +
> + kfree(efuse);
> + }
> +
> + return 0;
> +}
> +
> +static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
You did not test it, right? Build with section mismatch analysis...
> +{
> + u32 gt;
> +
> + gt = (*value) >> 24;
> +
> + if (gt && gt < LVTS_GOLDEN_TEMP_MAX)
> + golden_temp = gt;
> +
> + coeff_b = golden_temp * 500 + LVTS_COEFF_B;
> +
> + return 0;
> +}
> +
> +static int __init lvts_ctrl_init(struct device *dev,
Same problem.
> + struct lvts_domain *lvts_td,
> + struct lvts_data *lvts_data)
> +{
> + size_t size = sizeof(*lvts_td->lvts_ctrl) * lvts_data->num_lvts_ctrl;
> + struct lvts_ctrl *lvts_ctrl;
> + int i, ret;
> +
> +
> +static inline int lvts_ctrl_enable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + return lvts_ctrl_set_enable(lvts_ctrl, 1);
Drop the wrapper, it's useless. true or false for enable are quite obvious.
> +}
> +
> +static inline int lvts_ctrl_disable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + return lvts_ctrl_set_enable(lvts_ctrl, 0);
Drop the wrapper.
> +}
> +
> +static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 };
> +
> + lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
> +
> + /*
> + * LVTS_ID : Get ID and status of the thermal controller
> + *
> + * Bits:
> + *
> + * 0-5 : thermal controller id
> + * 7 : thermal controller connection is valid
> + */
> + id = readl(LVTS_ID(lvts_ctrl->base));
> + if (!(id & BIT(7)))
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + /*
> + * Write device mask: 0xC1030000
> + */
> + u32 cmds[] = {
> + 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
> + 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
> + 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
> + 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
> + };
> +
> + lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
> +
> + return 0;
> +}
> +
> +static int lvts_ctrl_calibrate(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + int i;
> + void __iomem *lvts_edata[] = {
> + LVTS_EDATA00(lvts_ctrl->base),
> + LVTS_EDATA01(lvts_ctrl->base),
> + LVTS_EDATA02(lvts_ctrl->base),
> + LVTS_EDATA03(lvts_ctrl->base)
> + };
> +
> + /*
> + * LVTS_EDATA0X : Efuse calibration reference value for sensor X
> + *
> + * Bits:
> + *
> + * 20-0 : Efuse value for normalization data
> + */
> + for (i = 0; i < LVTS_SENSOR_MAX; i++)
> + writel(lvts_ctrl->calibration[i], lvts_edata[i]);
> +
> + return 0;
> +}
> +
> +static int lvts_ctrl_configure(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + u32 value;
> +
> + /*
> + * LVTS_TSSEL : Sensing point index numbering
> + *
> + * Bits:
> + *
> + * 31-24: ADC Sense 3
> + * 23-16: ADC Sense 2
> + * 15-8 : ADC Sense 1
> + * 7-0 : ADC Sense 0
> + */
> + value = LVTS_TSSEL_CONF;
> + writel(value, LVTS_TSSEL(lvts_ctrl->base));
> +
> + /*
> + * LVTS_CALSCALE : ADC voltage round
> + */
> + value = 0x300;
> + value = LVTS_CALSCALE_CONF;
> +
> + /*
> + * LVTS_MSRCTL0 : Sensor filtering strategy
> + *
> + * Filters:
> + *
> + * 000 : One sample
> + * 001 : Avg 2 samples
> + * 010 : 4 samples, drop min and max, avg 2 samples
> + * 011 : 6 samples, drop min and max, avg 4 samples
> + * 100 : 10 samples, drop min and max, avg 8 samples
> + * 101 : 18 samples, drop min and max, avg 16 samples
> + *
> + * Bits:
> + *
> + * 0-2 : Sensor0 filter
> + * 3-5 : Sensor1 filter
> + * 6-8 : Sensor2 filter
> + * 9-11 : Sensor3 filter
> + */
> + value = LVTS_HW_FILTER << 9 | LVTS_HW_FILTER << 6 |
> + LVTS_HW_FILTER << 3 | LVTS_HW_FILTER;
> + writel(value, LVTS_MSRCTL0(lvts_ctrl->base));
> +
> + /*
> + * LVTS_MSRCTL1 : Measurement control
> + *
> + * Bits:
> + *
> + * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3
> + * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2
> + * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1
> + * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0
> + *
> + * That configuration will ignore the filtering and the delays
> + * introduced below in MONCTL1 and MONCTL2
> + */
> + if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) {
> + value = BIT(9) | BIT(6) | BIT(5) | BIT(4);
> + writel(value, LVTS_MSRCTL1(lvts_ctrl->base));
> + }
> +
> + /*
> + * LVTS_MONCTL1 : Period unit and group interval configuration
> + *
> + * The clock source of LVTS thermal controller is 26MHz.
> + *
> + * The period unit is a time base for all the interval delays
> + * specified in the registers. By default we use 12. The time
> + * conversion is done by multiplying by 256 and 1/26.10^6
> + *
> + * An interval delay multiplied by the period unit gives the
> + * duration in seconds.
> + *
> + * - Filter interval delay is a delay between two samples of
> + * the same sensor.
> + *
> + * - Sensor interval delay is a delay between two samples of
> + * different sensors.
> + *
> + * - Group interval delay is a delay between different rounds.
> + *
> + * For example:
> + * If Period unit = C, filter delay = 1, sensor delay = 2, group delay = 1,
> + * and two sensors, TS1 and TS2, are in a LVTS thermal controller
> + * and then
> + * Period unit time = C * 1/26M * 256 = 12 * 38.46ns * 256 = 118.149us
> + * Filter interval delay = 1 * Period unit = 118.149us
> + * Sensor interval delay = 2 * Period unit = 236.298us
> + * Group interval delay = 1 * Period unit = 118.149us
> + *
> + * TS1 TS1 ... TS1 TS2 TS2 ... TS2 TS1...
> + * <--> Filter interval delay
> + * <--> Sensor interval delay
> + * <--> Group interval delay
> + * Bits:
> + * 29 - 20 : Group interval
> + * 16 - 13 : Send a single interrupt when crossing the hot threshold (1)
> + * or an interrupt everytime the hot threshold is crossed (0)
> + * 9 - 0 : Period unit
> + *
> + */
> + value = LVTS_GROUP_INTERVAL << 20 | LVTS_PERIOD_UNIT;
> + writel(value, LVTS_MONCTL1(lvts_ctrl->base));
> +
> + /*
> + * LVTS_MONCTL2 : Filtering and sensor interval
> + *
> + * Bits:
> + *
> + * 25-16 : Interval unit in PERIOD_UNIT between sample on
> + * the same sensor, filter interval
> + * 9-0 : Interval unit in PERIOD_UNIT between each sensor
> + *
> + */
> + value = LVTS_FILTER_INTERVAL << 16 | LVTS_SENSOR_INTERVAL;
> + writel(value, LVTS_MONCTL2(lvts_ctrl->base));
> +
> + return lvts_irq_init(lvts_ctrl);
> +}
> +
> +static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> +{
> + struct lvts_sensor *lvts_sensors = lvts_ctrl->sensors;
> + struct thermal_zone_device *tz;
> + u32 sensor_map = 0;
> + int i;
> +
> + for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) {
> +
> + int dt_id = lvts_sensors[i].dt_id;
> +
> + tz = devm_thermal_of_zone_register(dev, dt_id, &lvts_sensors[i],
> + &lvts_ops);
> + if (IS_ERR(tz)) {
> + /*
> + * This thermal zone is not described in the
> + * device tree. It is not an error from the
> + * thermal OF code POV, we just continue.
> + */
> + if (PTR_ERR(tz) == -ENODEV)
> + continue;
> +
> + return PTR_ERR(tz);
> + }
> +
> + /*
> + * The thermal zone pointer will be needed in the
> + * interrupt handler, we store it in the sensor
> + * structure. The thermal domain structure will be
> + * passed to the interrupt handler private data as the
> + * interrupt is shared for all the controller
> + * belonging to the thermal domain.
> + */
> + lvts_sensors[i].tz = tz;
> +
> + /*
> + * This sensor was correctly associated with a thermal
> + * zone, let's set the corresponding bit in the sensor
> + * map, so we can enable the temperature monitoring in
> + * the hardware thermal controller.
> + */
> + sensor_map |= BIT(i);
> + }
> +
> + /*
> + * Bits:
> + * 9: Single point access flow
> + * 0-3: Enable sensing point 0-3
> + *
> + * The initialization of the thermal zones give us
> + * which sensor point to enable. If any thermal zone
> + * was not described in the device tree, it won't be
> + * enabled here in the sensor map.
> + */
> + writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
> +
> + return 0;
> +}
> +
> +static int lvts_domain_init(struct device *dev, struct lvts_domain *lvts_td,
> + struct lvts_data *lvts_data)
> +{
> + struct lvts_ctrl *lvts_ctrl;
> + int i, ret;
> +
> + ret = lvts_ctrl_init(dev, lvts_td, lvts_data);
> + if (ret)
> + return ret;
> +
> + ret = lvts_domain_reset(dev, lvts_td->reset);
> + if (ret) {
> + dev_dbg(dev, "Failed to reset domain");
> + return ret;
> + }
> +
> + for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
> +
> + lvts_ctrl = &lvts_td->lvts_ctrl[i];
> +
> + /*
> + * Initialization steps:
> + *
> + * - Enable the clock
> + * - Connect to the LVTS
> + * - Initialize the LVTS
> + * - Prepare the calibration data
> + * - Select monitored sensors
> + * [ Configure sampling ]
> + * [ Configure the interrupt ]
> + * - Start measurement
> + */
> + ret = lvts_ctrl_enable(dev, lvts_ctrl);
> + if (ret) {
> + dev_dbg(dev, "Failed to enable LVTS clock");
> + return ret;
> + }
> +
> + ret = lvts_ctrl_connect(dev, lvts_ctrl);
> + if (ret) {
> + dev_dbg(dev, "Failed to connect to LVTS controller");
> + return ret;
> + }
> +
> + ret = lvts_ctrl_initialize(dev, lvts_ctrl);
> + if (ret) {
> + dev_dbg(dev, "Failed to initialize controller");
> + return ret;
> + }
> +
> + ret = lvts_ctrl_calibrate(dev, lvts_ctrl);
> + if (ret) {
> + dev_dbg(dev, "Failed to calibrate controller");
> + return ret;
> + }
> +
> + ret = lvts_ctrl_configure(dev, lvts_ctrl);
> + if (ret) {
> + dev_dbg(dev, "Failed to configure controller");
> + return ret;
> + }
> +
> + ret = lvts_ctrl_start(dev, lvts_ctrl);
> + if (ret) {
> + dev_dbg(dev, "Failed to start controller");
> + return ret;
> + }
> + }
> +
> + return lvts_debugfs_init(dev, lvts_td);
> +}
> +
> +static int lvts_probe(struct platform_device *pdev)
> +{
> + struct lvts_data *lvts_data;
> + struct lvts_domain *lvts_td;
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + int irq, ret;
> +
> + lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL);
> + if (!lvts_td)
> + return -ENOMEM;
> +
> + lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
Why do you need case?
> + if (!lvts_data) {
> + dev_dbg(dev, "No platforme
Drop. How is it even possible?
> + return -ENODATA;
> + };
> +
> + lvts_td->clk = devm_clk_get_enabled(dev, NULL);
> + if (IS_ERR(lvts_td->clk)) {
> + dev_dbg(dev, "Failed to retrieve clock\n");
Drop all debug statements. Either this is an error (so return
dev_err_probe) or core handles messages.
> + return PTR_ERR(lvts_td->clk);
> + }
> +
> + res = platform_get_mem_or_io(pdev, 0);
> + if (!res) {
> + dev_dbg(dev, "No IO resource\n");
Ditto
> + return -ENXIO;
> + }
> +
> + lvts_td->base = devm_ioremap_resource(dev, res);
Why not using single wrapper for this?
> + if (IS_ERR(lvts_td->base)) {
> + dev_dbg(dev, "Failed to map io resource\n");
Ditto
> + return PTR_ERR(lvts_td->base);
> + }
> +
> + lvts_td->reset = devm_reset_control_get_by_index(dev, 0);
> + if (IS_ERR(lvts_td->reset)) {
> + dev_dbg(dev, "Failed to get reset control\n");
Ditto
> + return PTR_ERR(lvts_td->reset);
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_dbg(dev, "No irq resource\n");
Ditto
> + return irq;
> + }
> +
> + ret = lvts_domain_init(dev, lvts_td, lvts_data);
> + if (ret) {
> + dev_dbg(dev, "Failed to initialize the lvts domain\n");
Why this is debug?
> + return ret;
> + }
> +
> + /*
> + * At this point the LVTS is initialized and enabled. We can
> + * safely enable the interrupt.
> + */
> + ret = devm_request_threaded_irq(dev, irq, NULL, lvts_irq_handler,
> + IRQF_ONESHOT, dev_name(dev), lvts_td);
> + if (ret) {
> + dev_dbg(dev, "Failed to request interrupt\n");
Ditto
> + return ret;
> + }
> +
> + platform_set_drvdata(pdev, lvts_td);
> +
> + return 0;
> +}
> +
> +static int lvts_remove(struct platform_device *pdev)
> +{
> + struct lvts_domain *lvts_td;
> + struct device *dev = &pdev->dev;
> + int i;
> +
> + lvts_td = platform_get_drvdata(pdev);
> +
> + for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
> + lvts_ctrl_disable(dev, &lvts_td->lvts_ctrl[i]);
> +
> + lvts_debugfs_exit();
> +
> + return 0;
> +}
> +
> +static struct lvts_ctrl_data mt8195_lvts_data_ctrl[] = {
Why this cannot be const?
> + {
> + .cal_offset = { 0x4, 0x7 },
> + .lvts_sensor = {
> + { .dt_id = MT8195_MCU_BIG_CPU0 },
> + { .dt_id = MT8195_MCU_BIG_CPU1 }
> + },
> + .num_lvts_sensor = 2,
> + .offset = 0x0,
> + .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
> + },
> +
Drop blank line
> + {
Best regards,
Krzysztof
Hi Krzysztof
On Wed, Feb 1, 2023 at 8:46 AM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 31/01/2023 15:04, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
> > Add LVTS thermal controllers dt-binding definition for mt8195.
> >
> > Signed-off-by: Balsam CHIHI <[email protected]>
> > ---
> > Changelog:
> > v3:
> > - Fixed subject prefix
> > - Fixed licenses GPL-2.0-only OR BSD-2-Clause
> > to GPL-2.0 OR MIT (to match DT)
> > - Fixed matching dt-binding file names
>
> If this is v3 with only one patch, where is the driver or DTS using
> these bindings? Please link it. It's unusual to see only bindings,
> without the users.
sorry, I'll be careful next time.
and I will take into account the new change requests in the next full
version of the series.
I apologize for the mess.
>
> > v2:
> > - Fixed subject prefix
> > - Fixed licenses GPL-2.0+ to GPL-2.0
> > - Added dual licenses
>
> Is there a reason to make our review more difficult and keep versions
> broken, threads attached to some other threads?
sorry again.
>
> ------------
>
> Grabbing thread from
> lore.kernel.org/all/20230131140439.600164-1-bchihi%40baylibre.com/t.mbox.gz
> Checking for newer revisions on https://lore.kernel.org/all/
> Analyzing 38 messages in the thread
> Will use the latest revision: v12
> You can pick other revisions using the -vN flag
> Checking attestation on all messages, may take a moment...
> ---
> ✓ [PATCH v12] thermal: drivers: mediatek: Add the Low Voltage Thermal
> Sensor driver
> ✓ Signed: DKIM/baylibre-com.20210112.gappssmtp.com (From:
> [email protected])
> + Link:
> https://lore.kernel.org/r/[email protected]
> ✓ [PATCH v12 2/6] dt-bindings: thermal: mediatek: Add LVTS thermal
> controllers dt-binding definition
> ✓ Signed: DKIM/baylibre-com.20210112.gappssmtp.com (From:
> [email protected])
> + Link:
> https://lore.kernel.org/r/[email protected]
> ERROR: missing [3/1]!
> ERROR: missing [4/1]!
> ERROR: missing [5/1]!
> ERROR: missing [6/1]!
>
> --------
>
> b4 diff '<[email protected]>'
> Checking for older revisions on https://lore.kernel.org/all/
> ---
> Analyzing 38 messages in the thread
> Assuming new revision: v4 ([PATCH v12] thermal: drivers: mediatek: Add
> the Low Voltage Thermal Sensor driver)
> Preparing fake-am for v3: dt-bindings: thermal: mediatek: Add LVTS
> thermal controllers
> range: 291580cde5f6..de7fe5e0293a
> Preparing fake-am for v12: arm64: dts: mediatek: mt8195: Add thermal
> zones and thermal nodes
> ERROR: Could not find matching blob for
> arch/arm64/boot/dts/mediatek/mt8195.dtsi (09df105f4606)
> If you know on which tree this patchset is based,
> add it as a remote and perform "git remote update"
> in order to fetch the missing objects.
> ---
> Could not create fake-am range for upper series v12
>
>
> > ---
> > ---
> > .../thermal/mediatek,lvts-thermal.yaml | 107 ++++++++++++++++++
> > .../thermal/mediatek,lvts-thermal.h | 19 ++++
> > 2 files changed, 126 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> > create mode 100644 include/dt-bindings/thermal/mediatek,lvts-thermal.h
> >
> > diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> > new file mode 100644
> > index 000000000000..5fa5c7a1a417
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> > @@ -0,0 +1,107 @@
> > +# SPDX-License-Identifier: (GPL-2.0 OR MIT)
>
> WARNING: DT binding documents should be licensed (GPL-2.0-only OR
> BSD-2-Clause)
> #24: FILE:
> Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml:1:
> +# SPDX-License-Identifier: (GPL-2.0 OR MIT)
>
> I asked you to use the binding license for header file. Then you changed
> binding license... why? Why do you need other SPDX text? Why do you need
> MIT?
I will put it back "GPL-2.0-only OR BSD-2-Clause" in the binding and
do the same for the header.
>
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
> > +
> > +maintainers:
> > + - Balsam CHIHI <[email protected]>
> > +
> > +description: |
> > + LVTS is a thermal management architecture composed of three subsystems,
> > + a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
> > + a Converter - Low Voltage Thermal Sensor converter (LVTS), and
> > + a Digital controller (LVTS_CTRL).
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - mediatek,mt8195-lvts-ap
> > + - mediatek,mt8195-lvts-mcu
>
> What about other devices? You called the file name as generic for all
> Mediatek SoCs, so why only one SoC is here? Is there going to be more?
> If yes, why they cannot be added now?
Yes, there is another MTK SoC mt8192 that supports LVTS,
I was asked in v10 of the series to remove the unimplemented SoC.
It will be added later with the driver that supports it.
just let me know if you still want to add mt8192 bindings in the next
version without the driver.
(LVTS support for mt8192 will be sent in a different series).
>
> Best regards,
> Krzysztof
>
On 01/02/2023 14:34, Balsam CHIHI wrote:
>>> +
>>> +properties:
>>> + compatible:
>>> + enum:
>>> + - mediatek,mt8195-lvts-ap
>>> + - mediatek,mt8195-lvts-mcu
>>
>> What about other devices? You called the file name as generic for all
>> Mediatek SoCs, so why only one SoC is here? Is there going to be more?
>> If yes, why they cannot be added now?
>
> Yes, there is another MTK SoC mt8192 that supports LVTS,
> I was asked in v10 of the series to remove the unimplemented SoC.
> It will be added later with the driver that supports it.
> just let me know if you still want to add mt8192 bindings in the next
> version without the driver.
The binding should be complete, if that's possible, so if you had mt8192
already there, it could stay. Anyway it's fine then.
Best regards,
Krzysztof
Hi Krzysztof,
On Wed, Feb 1, 2023 at 2:37 PM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 01/02/2023 14:34, Balsam CHIHI wrote:
> >>> +
> >>> +properties:
> >>> + compatible:
> >>> + enum:
> >>> + - mediatek,mt8195-lvts-ap
> >>> + - mediatek,mt8195-lvts-mcu
> >>
> >> What about other devices? You called the file name as generic for all
> >> Mediatek SoCs, so why only one SoC is here? Is there going to be more?
> >> If yes, why they cannot be added now?
> >
> > Yes, there is another MTK SoC mt8192 that supports LVTS,
> > I was asked in v10 of the series to remove the unimplemented SoC.
> > It will be added later with the driver that supports it.
> > just let me know if you still want to add mt8192 bindings in the next
> > version without the driver.
>
> The binding should be complete, if that's possible, so if you had mt8192
> already there, it could stay. Anyway it's fine then.
OK, I will put back mt8192 dt-bindings.
this is the link to the v10 patch
"https://patchwork.kernel.org/project/linux-pm/patch/[email protected]/",
it will be the same in next version (v13) of series.
it would be great if you review it in advance, so I could take into
account the new changes if needed.
is it possible to resend this patch under v13, to simplify the review
and avoid breaking the series again?
>
> Best regards,
> Krzysztof
>
Best regards,
Balsam
Hi Krzysztof,
On Wed, Feb 1, 2023 at 8:47 AM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 01/02/2023 04:09, kernel test robot wrote:
> > Hi Balsam,
> >
> > Thank you for the patch! Yet something to improve:
> >
> >
> >
> > url: https://github.com/intel-lab-lkp/linux/commits/UPDATE-20230131-234122/bchihi-baylibre-com/thermal-drivers-mediatek-Relocate-driver-to-mediatek-folder/20230124-211910
> > base: the 4th patch of https://lore.kernel.org/r/20230124131717.128660-5-bchihi%40baylibre.com
> > patch link: https://lore.kernel.org/r/20230131153816.21709-1-bchihi%40baylibre.com
> > patch subject: [PATCH v12] thermal: drivers: mediatek: Add the Low Voltage Thermal Sensor driver
> > config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20230201/[email protected]/config)
> > compiler: s390-linux-gcc (GCC) 12.1.0
> > reproduce (this is a W=1 build):
> > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> > chmod +x ~/bin/make.cross
> > # https://github.com/intel-lab-lkp/linux/commit/5646ebf6f10ff0fc60c04d8c57523f7c44526b41
> > git remote add linux-review https://github.com/intel-lab-lkp/linux
> > git fetch --no-tags linux-review UPDATE-20230131-234122/bchihi-baylibre-com/thermal-drivers-mediatek-Relocate-driver-to-mediatek-folder/20230124-211910
> > git checkout 5646ebf6f10ff0fc60c04d8c57523f7c44526b41
> > # save the config file
> > mkdir build_dir && cp config build_dir/.config
> > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 olddefconfig
> > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=s390 SHELL=/bin/bash
> >
> > If you fix the issue, kindly add following tag where applicable
> > | Reported-by: kernel test robot <[email protected]>
> >
> > All errors (new ones prefixed by >>):
> >
> >>> drivers/thermal/mediatek/lvts_thermal.c:20:10: fatal error: dt-bindings/thermal/mediatek,lvts-thermal.h: No such file or directory
> > 20 | #include <dt-bindings/thermal/mediatek,lvts-thermal.h>
> > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> So here is your driver... and you decided not only to make reviewers
> life more difficult, but also to fail all automated tools.
>
> No, that's not how patchsets should be sent. You have here clear
> dependency, you cannot send them separately.
I'm sorry, I'll be careful next time.
I apologize for breaking the series.
I will take into account the new change requests in the next full
v13 the series.
>
> Best regards,
> Krzysztof
>
Best regards,
Balsam
Hi Krzysztof,
Thank you very much for the review!
On Wed, Feb 1, 2023 at 8:55 AM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 31/01/2023 16:38, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
> > The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
> > controllers contained in a thermal domain.
> >
> > A thermal domains can be the MCU or the AP.
> >
> > Each thermal domains contain up to seven controllers, each thermal
> > controller handle up to four thermal sensors.
> >
> > The LVTS has two Finite State Machines (FSM), one to handle the
> > functionin temperatures range like hot or cold temperature and another
> > one to handle monitoring trip point. The FSM notifies via interrupts
> > when a trip point is crossed.
> >
>
> (...)
>
> > +
> > +struct lvts_domain {
> > + struct lvts_ctrl *lvts_ctrl;
> > + struct reset_control *reset;
> > + struct clk *clk;
> > + int num_lvts_ctrl;
> > + void __iomem *base;
> > + size_t calib_len;
> > + u8 *calib;
> > +};
> > +
> > +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
> > +
> > +static struct dentry *root;
>
> How do you handle two instances of driver?
This root node is the topmost directory for debugfs called 'lvts', the
different driver instances are below this. It is a singleton.
>
> > +
> > +#define LVTS_DEBUG_FS_REGS(__reg) \
> > +{ \
> > + .name = __stringify(__reg), \
> > + .offset = __reg(0), \
> > +}
> > +
>
> (...)
>
> > +
> > +static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high)
> > +{
> > + struct lvts_sensor *lvts_sensor = tz->devdata;
> > + void __iomem *base = lvts_sensor->base;
> > + u32 raw_low = lvts_temp_to_raw(low);
> > + u32 raw_high = lvts_temp_to_raw(high);
> > +
> > + /*
> > + * Hot to normal temperature threshold
> > + *
> > + * LVTS_H2NTHRE
> > + *
> > + * Bits:
> > + *
> > + * 14-0 : Raw temperature for threshold
> > + */
> > + if (low != -INT_MAX) {
> > + dev_dbg(&tz->device, "Setting low limit temperature interrupt: %d\n", low);
> > + writel(raw_low, LVTS_H2NTHRE(base));
> > + }
> > +
> > + /*
> > + * Hot temperature threshold
> > + *
> > + * LVTS_HTHRE
> > + *
> > + * Bits:
> > + *
> > + * 14-0 : Raw temperature for threshold
> > + */
> > + dev_dbg(&tz->device, "Setting high limit temperature interrupt: %d\n", high);
> > + writel(raw_high, LVTS_HTHRE(base));
> > +
> > + return 0;
> > +}
> > +
> > +static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl)
> > +{
> > + irqreturn_t iret = IRQ_NONE;
> > + u32 value, masks[] = {
>
> Don't mix different types in one declaration. u32 and a pointer are
> quite different types.
I'm not sure to understand.
LVTS_INT_SENSORx are not pointers but register values.
>
> > + LVTS_INT_SENSOR0,
> > + LVTS_INT_SENSOR1,
> > + LVTS_INT_SENSOR2,
> > + LVTS_INT_SENSOR3
> > + };
> > + int i;
> > +
> > + /*
> > + * Interrupt monitoring status
> > + *
> > + * LVTS_MONINTST
> > + *
> > + * Bits:
> > + *
> > + * 31 : Interrupt for stage 3
> > + * 30 : Interrupt for stage 2
> > + * 29 : Interrupt for state 1
> > + * 28 : Interrupt using filter on sensor 3
> > + *
> > + * 27 : Interrupt using immediate on sensor 3
> > + * 26 : Interrupt normal to hot on sensor 3
> > + * 25 : Interrupt high offset on sensor 3
> > + * 24 : Interrupt low offset on sensor 3
> > + *
> > + * 23 : Interrupt hot threshold on sensor 3
> > + * 22 : Interrupt cold threshold on sensor 3
> > + * 21 : Interrupt using filter on sensor 2
> > + * 20 : Interrupt using filter on sensor 1
> > + *
> > + * 19 : Interrupt using filter on sensor 0
> > + * 18 : Interrupt using immediate on sensor 2
> > + * 17 : Interrupt using immediate on sensor 1
> > + * 16 : Interrupt using immediate on sensor 0
> > + *
> > + * 15 : Interrupt device access timeout interrupt
> > + * 14 : Interrupt normal to hot on sensor 2
> > + * 13 : Interrupt high offset interrupt on sensor 2
> > + * 12 : Interrupt low offset interrupt on sensor 2
> > + *
> > + * 11 : Interrupt hot threshold on sensor 2
> > + * 10 : Interrupt cold threshold on sensor 2
> > + * 9 : Interrupt normal to hot on sensor 1
> > + * 8 : Interrupt high offset interrupt on sensor 1
> > + *
> > + * 7 : Interrupt low offset interrupt on sensor 1
> > + * 6 : Interrupt hot threshold on sensor 1
> > + * 5 : Interrupt cold threshold on sensor 1
> > + * 4 : Interrupt normal to hot on sensor 0
> > + *
> > + * 3 : Interrupt high offset interrupt on sensor 0
> > + * 2 : Interrupt low offset interrupt on sensor 0
> > + * 1 : Interrupt hot threshold on sensor 0
> > + * 0 : Interrupt cold threshold on sensor 0
> > + *
> > + * We are interested in the sensor(s) responsible of the
> > + * interrupt event. We update the thermal framework with the
> > + * thermal zone associated with the sensor. The framework will
> > + * take care of the rest whatever the kind of interrupt, we
> > + * are only interested in which sensor raised the interrupt.
> > + *
> > + * sensor 3 interrupt: 0001 1111 1100 0000 0000 0000 0000 0000
> > + * => 0x1FC00000
> > + * sensor 2 interrupt: 0000 0000 0010 0100 0111 1100 0000 0000
> > + * => 0x00247C00
> > + * sensor 1 interrupt: 0000 0000 0001 0001 0000 0011 1110 0000
> > + * => 0X000881F0
> > + * sensor 0 interrupt: 0000 0000 0000 1001 0000 0000 0001 1111
> > + * => 0x0009001F
> > + */
> > + value = readl(LVTS_MONINTSTS(lvts_ctrl->base));
> > +
> > + /*
> > + * Let's figure out which sensors raised the interrupt
> > + *
> > + * NOTE: the masks array must be ordered with the index
> > + * corresponding to the sensor id eg. index=0, mask for
> > + * sensor0.
> > + */
> > + for (i = 0; i < ARRAY_SIZE(masks); i++) {
> > +
> > + if (!(value & masks[i]))
> > + continue;
> > +
> > + thermal_zone_device_update(lvts_ctrl->sensors[i].tz,
> > + THERMAL_TRIP_VIOLATED);
> > + iret = IRQ_HANDLED;
> > + }
> > +
> > + /*
> > + * Write back to clear the interrupt status (W1C)
> > + */
> > + writel(value, LVTS_MONINTSTS(lvts_ctrl->base));
> > +
> > + return iret;
> > +}
> > +
> > +/*
> > + * Temperature interrupt handler. Even if the driver supports more
> > + * interrupt modes, we use the interrupt when the temperature crosses
> > + * the hot threshold the way up and the way down (modulo the
> > + * hysteresis).
> > + *
> > + * Each thermal domain has a couple of interrupts, one for hardware
> > + * reset and another one for all the thermal events happening on the
> > + * different sensors.
> > + *
> > + * The interrupt is configured for thermal events when crossing the
> > + * hot temperature limit. At each interrupt, we check in every
> > + * controller if there is an interrupt pending.
> > + */
> > +static irqreturn_t lvts_irq_handler(int irq, void *data)
> > +{
> > + struct lvts_domain *lvts_td = data;
> > + irqreturn_t aux, iret = IRQ_NONE;
> > + int i;
> > +
> > + for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
> > +
> > + aux = lvts_ctrl_irq_handler(lvts_td->lvts_ctrl);
> > + if (aux != IRQ_HANDLED)
> > + continue;
> > +
> > + iret = IRQ_HANDLED;
> > + }
> > +
> > + return iret;
> > +}
> > +
> > +static struct thermal_zone_device_ops lvts_ops = {
> > + .get_temp = lvts_get_temp,
> > + .set_trips = lvts_set_trips,
> > +};
> > +
> > +static int __init lvts_sensor_init(struct device *dev,
> > + struct lvts_ctrl *lvts_ctrl,
> > + struct lvts_ctrl_data *lvts_ctrl_data)
> > +{
> > + struct lvts_sensor *lvts_sensor = lvts_ctrl->sensors;
> > + void __iomem *msr_regs[] = {
> > + LVTS_MSR0(lvts_ctrl->base),
> > + LVTS_MSR1(lvts_ctrl->base),
> > + LVTS_MSR2(lvts_ctrl->base),
> > + LVTS_MSR3(lvts_ctrl->base)
> > + };
> > +
> > + void __iomem *imm_regs[] = {
> > + LVTS_IMMD0(lvts_ctrl->base),
> > + LVTS_IMMD1(lvts_ctrl->base),
> > + LVTS_IMMD2(lvts_ctrl->base),
> > + LVTS_IMMD3(lvts_ctrl->base)
> > + };
> > +
> > + int i;
> > +
> > + for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) {
> > +
> > + int dt_id = lvts_ctrl_data->lvts_sensor[i].dt_id;
> > +
> > + /*
> > + * At this point, we don't know which id matches which
> > + * sensor. Let's set arbitrally the id from the index.
> > + */
> > + lvts_sensor[i].id = i;
> > +
> > + /*
> > + * The thermal zone registration will set the trip
> > + * point interrupt in the thermal controller
> > + * register. But this one will be reset in the
> > + * initialization after. So we need to post pone the
> > + * thermal zone creation after the controller is
> > + * setup. For this reason, we store the device tree
> > + * node id from the data in the sensor structure
> > + */
> > + lvts_sensor[i].dt_id = dt_id;
> > +
> > + /*
> > + * We assign the base address of the thermal
> > + * controller as a back pointer. So it will be
> > + * accessible from the different thermal framework ops
> > + * as we pass the lvts_sensor pointer as thermal zone
> > + * private data.
> > + */
> > + lvts_sensor[i].base = lvts_ctrl->base;
> > +
> > + /*
> > + * Each sensor has its own register address to read from.
> > + */
> > + lvts_sensor[i].msr = lvts_ctrl_data->mode == LVTS_MSR_IMMEDIATE_MODE ?
> > + imm_regs[i] : msr_regs[i];
> > + };
> > +
> > + lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor;
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * The efuse blob values follows the sensor enumeration per thermal
> > + * controller. The decoding of the stream is as follow:
> > + *
> > + * <--?-> <----big0 ???---> <-sensor0-> <-0->
> > + * ------------------------------------------
> > + * index in the stream: : | 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 |
> > + * ------------------------------------------
> > + *
> > + * <--sensor1--><-0-> <----big1 ???---> <-sen
> > + * ------------------------------------------
> > + * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
> > + * ------------------------------------------
> > + *
> > + * sor0-> <-0-> <-sensor1-> <-0-> ..........
> > + * ------------------------------------------
> > + * | 0x7 | 0x8 | 0x9 | 0xA | 0xB | OxC | OxD |
> > + * ------------------------------------------
> > + *
> > + * And so on ...
> > + *
> > + * The data description gives the offset of the calibration data in
> > + * this bytes stream for each sensor.
> > + *
> > + * Each thermal controller can handle up to 4 sensors max, we don't
> > + * care if there are less as the array of calibration is sized to 4
> > + * anyway. The unused sensor slot will be zeroed.
> > + */
> > +static int __init lvts_calibration_init(struct device *dev,
> > + struct lvts_ctrl *lvts_ctrl,
> > + struct lvts_ctrl_data *lvts_ctrl_data,
> > + u8 *efuse_calibration)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++)
> > + memcpy(&lvts_ctrl->calibration[i],
> > + efuse_calibration + lvts_ctrl_data->cal_offset[i], 2);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * The efuse bytes stream can be split into different chunk of
> > + * nvmems. This function reads and concatenate those into a single
> > + * buffer so it can be read sequentially when initializing the
> > + * calibration data.
> > + */
> > +static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td,
> > + struct lvts_data *lvts_data)
> > +{
> > + struct device_node *np = dev_of_node(dev);
> > + struct nvmem_cell *cell;
> > + struct property *prop;
> > + const char *cell_name;
> > +
> > + of_property_for_each_string(np, "nvmem-cell-names", prop, cell_name) {
> > + size_t len;
> > + u8 *efuse;
> > +
> > + cell = of_nvmem_cell_get(np, cell_name);
> > + if (IS_ERR(cell)) {
> > + dev_dbg(dev, "Failed to get cell '%s'\n", cell_name);
>
> Is this an error? If so, why debug? dbg is not for errors.
AFAIK using dev_dbg does not increase ELF size when DEBUG is disabled.
If this is not a good reason for you, then I will change it to dev_err.
>
> > + return PTR_ERR(cell);
> > + }
> > +
> > + efuse = nvmem_cell_read(cell, &len);
> > +
> > + nvmem_cell_put(cell);
> > +
> > + if (IS_ERR(efuse)) {
> > + dev_dbg(dev, "Failed to read cell '%s'\n", cell_name);
> > + return PTR_ERR(efuse);
> > + }
> > +
> > + lvts_td->calib = devm_krealloc(dev, lvts_td->calib,
> > + lvts_td->calib_len + len, GFP_KERNEL);
> > + if (!lvts_td->calib)
> > + return -ENOMEM;
> > +
> > + memcpy(lvts_td->calib + lvts_td->calib_len, efuse, len);
> > +
> > + lvts_td->calib_len += len;
> > +
> > + kfree(efuse);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
>
> You did not test it, right? Build with section mismatch analysis...
I'm not sure to fully understand this comment.
Would you explain, please?
>
> > +{
> > + u32 gt;
> > +
> > + gt = (*value) >> 24;
> > +
> > + if (gt && gt < LVTS_GOLDEN_TEMP_MAX)
> > + golden_temp = gt;
> > +
> > + coeff_b = golden_temp * 500 + LVTS_COEFF_B;
> > +
> > + return 0;
> > +}
> > +
> > +static int __init lvts_ctrl_init(struct device *dev,
>
> Same problem.
Would you explain, please?
>
> > + struct lvts_domain *lvts_td,
> > + struct lvts_data *lvts_data)
> > +{
> > + size_t size = sizeof(*lvts_td->lvts_ctrl) * lvts_data->num_lvts_ctrl;
> > + struct lvts_ctrl *lvts_ctrl;
> > + int i, ret;
> > +
>
> > +
> > +static inline int lvts_ctrl_enable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + return lvts_ctrl_set_enable(lvts_ctrl, 1);
>
> Drop the wrapper, it's useless. true or false for enable are quite obvious.
OK.
>
> > +}
> > +
> > +static inline int lvts_ctrl_disable(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + return lvts_ctrl_set_enable(lvts_ctrl, 0);
>
> Drop the wrapper.
OK.
>
> > +}
> > +
> > +static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 };
> > +
> > + lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
> > +
> > + /*
> > + * LVTS_ID : Get ID and status of the thermal controller
> > + *
> > + * Bits:
> > + *
> > + * 0-5 : thermal controller id
> > + * 7 : thermal controller connection is valid
> > + */
> > + id = readl(LVTS_ID(lvts_ctrl->base));
> > + if (!(id & BIT(7)))
> > + return -EIO;
> > +
> > + return 0;
> > +}
> > +
> > +static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + /*
> > + * Write device mask: 0xC1030000
> > + */
> > + u32 cmds[] = {
> > + 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
> > + 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
> > + 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
> > + 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
> > + };
> > +
> > + lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
> > +
> > + return 0;
> > +}
> > +
> > +static int lvts_ctrl_calibrate(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + int i;
> > + void __iomem *lvts_edata[] = {
> > + LVTS_EDATA00(lvts_ctrl->base),
> > + LVTS_EDATA01(lvts_ctrl->base),
> > + LVTS_EDATA02(lvts_ctrl->base),
> > + LVTS_EDATA03(lvts_ctrl->base)
> > + };
> > +
> > + /*
> > + * LVTS_EDATA0X : Efuse calibration reference value for sensor X
> > + *
> > + * Bits:
> > + *
> > + * 20-0 : Efuse value for normalization data
> > + */
> > + for (i = 0; i < LVTS_SENSOR_MAX; i++)
> > + writel(lvts_ctrl->calibration[i], lvts_edata[i]);
> > +
> > + return 0;
> > +}
> > +
> > +static int lvts_ctrl_configure(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + u32 value;
> > +
> > + /*
> > + * LVTS_TSSEL : Sensing point index numbering
> > + *
> > + * Bits:
> > + *
> > + * 31-24: ADC Sense 3
> > + * 23-16: ADC Sense 2
> > + * 15-8 : ADC Sense 1
> > + * 7-0 : ADC Sense 0
> > + */
> > + value = LVTS_TSSEL_CONF;
> > + writel(value, LVTS_TSSEL(lvts_ctrl->base));
> > +
> > + /*
> > + * LVTS_CALSCALE : ADC voltage round
> > + */
> > + value = 0x300;
> > + value = LVTS_CALSCALE_CONF;
> > +
> > + /*
> > + * LVTS_MSRCTL0 : Sensor filtering strategy
> > + *
> > + * Filters:
> > + *
> > + * 000 : One sample
> > + * 001 : Avg 2 samples
> > + * 010 : 4 samples, drop min and max, avg 2 samples
> > + * 011 : 6 samples, drop min and max, avg 4 samples
> > + * 100 : 10 samples, drop min and max, avg 8 samples
> > + * 101 : 18 samples, drop min and max, avg 16 samples
> > + *
> > + * Bits:
> > + *
> > + * 0-2 : Sensor0 filter
> > + * 3-5 : Sensor1 filter
> > + * 6-8 : Sensor2 filter
> > + * 9-11 : Sensor3 filter
> > + */
> > + value = LVTS_HW_FILTER << 9 | LVTS_HW_FILTER << 6 |
> > + LVTS_HW_FILTER << 3 | LVTS_HW_FILTER;
> > + writel(value, LVTS_MSRCTL0(lvts_ctrl->base));
> > +
> > + /*
> > + * LVTS_MSRCTL1 : Measurement control
> > + *
> > + * Bits:
> > + *
> > + * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3
> > + * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2
> > + * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1
> > + * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0
> > + *
> > + * That configuration will ignore the filtering and the delays
> > + * introduced below in MONCTL1 and MONCTL2
> > + */
> > + if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) {
> > + value = BIT(9) | BIT(6) | BIT(5) | BIT(4);
> > + writel(value, LVTS_MSRCTL1(lvts_ctrl->base));
> > + }
> > +
> > + /*
> > + * LVTS_MONCTL1 : Period unit and group interval configuration
> > + *
> > + * The clock source of LVTS thermal controller is 26MHz.
> > + *
> > + * The period unit is a time base for all the interval delays
> > + * specified in the registers. By default we use 12. The time
> > + * conversion is done by multiplying by 256 and 1/26.10^6
> > + *
> > + * An interval delay multiplied by the period unit gives the
> > + * duration in seconds.
> > + *
> > + * - Filter interval delay is a delay between two samples of
> > + * the same sensor.
> > + *
> > + * - Sensor interval delay is a delay between two samples of
> > + * different sensors.
> > + *
> > + * - Group interval delay is a delay between different rounds.
> > + *
> > + * For example:
> > + * If Period unit = C, filter delay = 1, sensor delay = 2, group delay = 1,
> > + * and two sensors, TS1 and TS2, are in a LVTS thermal controller
> > + * and then
> > + * Period unit time = C * 1/26M * 256 = 12 * 38.46ns * 256 = 118.149us
> > + * Filter interval delay = 1 * Period unit = 118.149us
> > + * Sensor interval delay = 2 * Period unit = 236.298us
> > + * Group interval delay = 1 * Period unit = 118.149us
> > + *
> > + * TS1 TS1 ... TS1 TS2 TS2 ... TS2 TS1...
> > + * <--> Filter interval delay
> > + * <--> Sensor interval delay
> > + * <--> Group interval delay
> > + * Bits:
> > + * 29 - 20 : Group interval
> > + * 16 - 13 : Send a single interrupt when crossing the hot threshold (1)
> > + * or an interrupt everytime the hot threshold is crossed (0)
> > + * 9 - 0 : Period unit
> > + *
> > + */
> > + value = LVTS_GROUP_INTERVAL << 20 | LVTS_PERIOD_UNIT;
> > + writel(value, LVTS_MONCTL1(lvts_ctrl->base));
> > +
> > + /*
> > + * LVTS_MONCTL2 : Filtering and sensor interval
> > + *
> > + * Bits:
> > + *
> > + * 25-16 : Interval unit in PERIOD_UNIT between sample on
> > + * the same sensor, filter interval
> > + * 9-0 : Interval unit in PERIOD_UNIT between each sensor
> > + *
> > + */
> > + value = LVTS_FILTER_INTERVAL << 16 | LVTS_SENSOR_INTERVAL;
> > + writel(value, LVTS_MONCTL2(lvts_ctrl->base));
> > +
> > + return lvts_irq_init(lvts_ctrl);
> > +}
> > +
> > +static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl)
> > +{
> > + struct lvts_sensor *lvts_sensors = lvts_ctrl->sensors;
> > + struct thermal_zone_device *tz;
> > + u32 sensor_map = 0;
> > + int i;
> > +
> > + for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) {
> > +
> > + int dt_id = lvts_sensors[i].dt_id;
> > +
> > + tz = devm_thermal_of_zone_register(dev, dt_id, &lvts_sensors[i],
> > + &lvts_ops);
> > + if (IS_ERR(tz)) {
> > + /*
> > + * This thermal zone is not described in the
> > + * device tree. It is not an error from the
> > + * thermal OF code POV, we just continue.
> > + */
> > + if (PTR_ERR(tz) == -ENODEV)
> > + continue;
> > +
> > + return PTR_ERR(tz);
> > + }
> > +
> > + /*
> > + * The thermal zone pointer will be needed in the
> > + * interrupt handler, we store it in the sensor
> > + * structure. The thermal domain structure will be
> > + * passed to the interrupt handler private data as the
> > + * interrupt is shared for all the controller
> > + * belonging to the thermal domain.
> > + */
> > + lvts_sensors[i].tz = tz;
> > +
> > + /*
> > + * This sensor was correctly associated with a thermal
> > + * zone, let's set the corresponding bit in the sensor
> > + * map, so we can enable the temperature monitoring in
> > + * the hardware thermal controller.
> > + */
> > + sensor_map |= BIT(i);
> > + }
> > +
> > + /*
> > + * Bits:
> > + * 9: Single point access flow
> > + * 0-3: Enable sensing point 0-3
> > + *
> > + * The initialization of the thermal zones give us
> > + * which sensor point to enable. If any thermal zone
> > + * was not described in the device tree, it won't be
> > + * enabled here in the sensor map.
> > + */
> > + writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
> > +
> > + return 0;
> > +}
> > +
> > +static int lvts_domain_init(struct device *dev, struct lvts_domain *lvts_td,
> > + struct lvts_data *lvts_data)
> > +{
> > + struct lvts_ctrl *lvts_ctrl;
> > + int i, ret;
> > +
> > + ret = lvts_ctrl_init(dev, lvts_td, lvts_data);
> > + if (ret)
> > + return ret;
> > +
> > + ret = lvts_domain_reset(dev, lvts_td->reset);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to reset domain");
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
> > +
> > + lvts_ctrl = &lvts_td->lvts_ctrl[i];
> > +
> > + /*
> > + * Initialization steps:
> > + *
> > + * - Enable the clock
> > + * - Connect to the LVTS
> > + * - Initialize the LVTS
> > + * - Prepare the calibration data
> > + * - Select monitored sensors
> > + * [ Configure sampling ]
> > + * [ Configure the interrupt ]
> > + * - Start measurement
> > + */
> > + ret = lvts_ctrl_enable(dev, lvts_ctrl);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to enable LVTS clock");
> > + return ret;
> > + }
> > +
> > + ret = lvts_ctrl_connect(dev, lvts_ctrl);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to connect to LVTS controller");
> > + return ret;
> > + }
> > +
> > + ret = lvts_ctrl_initialize(dev, lvts_ctrl);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to initialize controller");
> > + return ret;
> > + }
> > +
> > + ret = lvts_ctrl_calibrate(dev, lvts_ctrl);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to calibrate controller");
> > + return ret;
> > + }
> > +
> > + ret = lvts_ctrl_configure(dev, lvts_ctrl);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to configure controller");
> > + return ret;
> > + }
> > +
> > + ret = lvts_ctrl_start(dev, lvts_ctrl);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to start controller");
> > + return ret;
> > + }
> > + }
> > +
> > + return lvts_debugfs_init(dev, lvts_td);
> > +}
> > +
> > +static int lvts_probe(struct platform_device *pdev)
> > +{
> > + struct lvts_data *lvts_data;
> > + struct lvts_domain *lvts_td;
> > + struct device *dev = &pdev->dev;
> > + struct resource *res;
> > + int irq, ret;
> > +
> > + lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL);
> > + if (!lvts_td)
> > + return -ENOMEM;
> > +
> > + lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
>
> Why do you need case?
Would you explain, please?
>
> > + if (!lvts_data) {
> > + dev_dbg(dev, "No platforme
>
> Drop. How is it even possible?
OK,
All "dev_dbg" in "probe" function will be replaced by "dev_err_probe"
>
> > + return -ENODATA;
> > + };
> > +
> > + lvts_td->clk = devm_clk_get_enabled(dev, NULL);
> > + if (IS_ERR(lvts_td->clk)) {
> > + dev_dbg(dev, "Failed to retrieve clock\n");
>
> Drop all debug statements. Either this is an error (so return
> dev_err_probe) or core handles messages.
I will change it to dev_err_probe.
>
> > + return PTR_ERR(lvts_td->clk);
> > + }
> > +
> > + res = platform_get_mem_or_io(pdev, 0);
> > + if (!res) {
> > + dev_dbg(dev, "No IO resource\n");
>
> Ditto
>
> > + return -ENXIO;
> > + }
> > +
> > + lvts_td->base = devm_ioremap_resource(dev, res);
>
> Why not using single wrapper for this?
OK,
I will chnagne it to "lvts_td->base =
devm_platform_get_and_ioremap_resource(pdev, 0, &res);"
>
> > + if (IS_ERR(lvts_td->base)) {
> > + dev_dbg(dev, "Failed to map io resource\n");
>
> Ditto
I will change it to dev_err_probe.
>
> > + return PTR_ERR(lvts_td->base);
> > + }
> > +
> > + lvts_td->reset = devm_reset_control_get_by_index(dev, 0);
> > + if (IS_ERR(lvts_td->reset)) {
> > + dev_dbg(dev, "Failed to get reset control\n");
>
> Ditto
I will change it to dev_err_probe.
>
> > + return PTR_ERR(lvts_td->reset);
> > + }
> > +
> > + irq = platform_get_irq(pdev, 0);
> > + if (irq < 0) {
> > + dev_dbg(dev, "No irq resource\n");
>
> Ditto
I will change it to dev_err_probe.
>
> > + return irq;
> > + }
> > +
> > + ret = lvts_domain_init(dev, lvts_td, lvts_data);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to initialize the lvts domain\n");
>
> Why this is debug?
I will change it to dev_err_probe.
>
> > + return ret;
> > + }
> > +
> > + /*
> > + * At this point the LVTS is initialized and enabled. We can
> > + * safely enable the interrupt.
> > + */
> > + ret = devm_request_threaded_irq(dev, irq, NULL, lvts_irq_handler,
> > + IRQF_ONESHOT, dev_name(dev), lvts_td);
> > + if (ret) {
> > + dev_dbg(dev, "Failed to request interrupt\n");
>
> Ditto
I will change it to dev_err_probe.
>
> > + return ret;
> > + }
> > +
> > + platform_set_drvdata(pdev, lvts_td);
> > +
> > + return 0;
> > +}
> > +
> > +static int lvts_remove(struct platform_device *pdev)
> > +{
> > + struct lvts_domain *lvts_td;
> > + struct device *dev = &pdev->dev;
> > + int i;
> > +
> > + lvts_td = platform_get_drvdata(pdev);
> > +
> > + for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
> > + lvts_ctrl_disable(dev, &lvts_td->lvts_ctrl[i]);
> > +
> > + lvts_debugfs_exit();
> > +
> > + return 0;
> > +}
> > +
> > +static struct lvts_ctrl_data mt8195_lvts_data_ctrl[] = {
>
> Why this cannot be const?
I've got the following warning when I added "const"
drivers/thermal/mediatek/lvts_thermal.c:1286:27: warning:
initialization discards ‘const’ qualifier from pointer target type
[-Wdiscarded-qualifiers]
1286 | .lvts_ctrl = mt8195_lvts_data_ctrl,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
Still working on it.
Any suggestion will be helpful, thanks!
>
> > + {
> > + .cal_offset = { 0x4, 0x7 },
> > + .lvts_sensor = {
> > + { .dt_id = MT8195_MCU_BIG_CPU0 },
> > + { .dt_id = MT8195_MCU_BIG_CPU1 }
> > + },
> > + .num_lvts_sensor = 2,
> > + .offset = 0x0,
> > + .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
> > + },
> > +
>
> Drop blank line
OK,
I will do the same for other blank lines.
>
> > + {
>
> Best regards,
> Krzysztof
>
Best regards,
Balsam.
On 01/02/2023 17:46, Balsam CHIHI wrote:
> Hi Krzysztof,
>
> Thank you very much for the review!
>
> On Wed, Feb 1, 2023 at 8:55 AM Krzysztof Kozlowski
> <[email protected]> wrote:
>>
>> On 31/01/2023 16:38, [email protected] wrote:
>>> From: Balsam CHIHI <[email protected]>
>>>
[...]
>>> +
>>> +static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl)
>>> +{
>>> + irqreturn_t iret = IRQ_NONE;
>>> + u32 value, masks[] = {
>>
>> Don't mix different types in one declaration. u32 and a pointer are
>> quite different types.
>
> I'm not sure to understand.
> LVTS_INT_SENSORx are not pointers but register values.
>
u32 mask[] is a pointer to a array of u32 values of undefined length.
[...]
>>> +static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
>>
>> You did not test it, right? Build with section mismatch analysis...
>
> I'm not sure to fully understand this comment.
> Would you explain, please?
>
AFAIU:
lvts_golden_temp_init() and lvts_ctrl_init() are called from a function that is
not in __init section:
lvts_domain_init()
So if you free up the first to functions after init but not the callers, things
can explote.
[...]
>>> +
>>> +static int lvts_probe(struct platform_device *pdev)
>>> +{
>>> + struct lvts_data *lvts_data;
>>> + struct lvts_domain *lvts_td;
>>> + struct device *dev = &pdev->dev;
>>> + struct resource *res;
>>> + int irq, ret;
>>> +
>>> + lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL);
>>> + if (!lvts_td)
>>> + return -ENOMEM;
>>> +
>>> + lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
>>
>> Why do you need case?
>
> Would you explain, please?
>
Typo by Krysztof, he meant the cast.
lvts_data = of_device_get_match_data(dev);
should be good enough.
Regards,
Matthias
On 01/02/2023 17:46, Balsam CHIHI wrote:
>>> +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
>>> +
>>> +static struct dentry *root;
>>
>> How do you handle two instances of driver?
>
> This root node is the topmost directory for debugfs called 'lvts', the
> different driver instances are below this. It is a singleton.
Indeed. What about removal? Aren't you remobing entire directory
structure on first device removal?
(...)
>>> +
>>> + of_property_for_each_string(np, "nvmem-cell-names", prop, cell_name) {
>>> + size_t len;
>>> + u8 *efuse;
>>> +
>>> + cell = of_nvmem_cell_get(np, cell_name);
>>> + if (IS_ERR(cell)) {
>>> + dev_dbg(dev, "Failed to get cell '%s'\n", cell_name);
>>
>> Is this an error? If so, why debug? dbg is not for errors.
>
> AFAIK using dev_dbg does not increase ELF size when DEBUG is disabled.
> If this is not a good reason for you, then I will change it to dev_err.
But also dev_dbg are not visible in error or warn level logs. If this is
not an error, then indeed dev_dbg could be fine. But errors should be
verbose.
>
>>
>>> + return PTR_ERR(cell);
>>> + }
>>> +
>>> + efuse = nvmem_cell_read(cell, &len);
>>> +
>>> + nvmem_cell_put(cell);
>>> +
>>> + if (IS_ERR(efuse)) {
>>> + dev_dbg(dev, "Failed to read cell '%s'\n", cell_name);
>>> + return PTR_ERR(efuse);
>>> + }
>>> +
>>> + lvts_td->calib = devm_krealloc(dev, lvts_td->calib,
>>> + lvts_td->calib_len + len, GFP_KERNEL);
>>> + if (!lvts_td->calib)
>>> + return -ENOMEM;
>>> +
>>> + memcpy(lvts_td->calib + lvts_td->calib_len, efuse, len);
>>> +
>>> + lvts_td->calib_len += len;
>>> +
>>> + kfree(efuse);
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
>>
>> You did not test it, right? Build with section mismatch analysis...
>
> I'm not sure to fully understand this comment.
> Would you explain, please?
git grep -i "section mismatch" leads to lib/Kconfig.debug and
DEBUG_SECTION_MISMATCH
(...)
>>> +static struct lvts_ctrl_data mt8195_lvts_data_ctrl[] = {
>>
>> Why this cannot be const?
>
> I've got the following warning when I added "const"
> drivers/thermal/mediatek/lvts_thermal.c:1286:27: warning:
> initialization discards ‘const’ qualifier from pointer target type
> [-Wdiscarded-qualifiers]
> 1286 | .lvts_ctrl = mt8195_lvts_data_ctrl,
> | ^~~~~~~~~~~~~~~~~~~~~~~~~
As with every const... Do you need lvts_ctrl to be non-const? If yes,
then how do you handle multiple devices (singleton)?
Best regards,
Krzysztof
Hi Matthias,
On Wed, Feb 1, 2023 at 5:59 PM Matthias Brugger <[email protected]> wrote:
>
>
>
> On 01/02/2023 17:46, Balsam CHIHI wrote:
> > Hi Krzysztof,
> >
> > Thank you very much for the review!
> >
> > On Wed, Feb 1, 2023 at 8:55 AM Krzysztof Kozlowski
> > <[email protected]> wrote:
> >>
> >> On 31/01/2023 16:38, [email protected] wrote:
> >>> From: Balsam CHIHI <[email protected]>
> >>>
>
> [...]
>
> >>> +
> >>> +static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl)
> >>> +{
> >>> + irqreturn_t iret = IRQ_NONE;
> >>> + u32 value, masks[] = {
> >>
> >> Don't mix different types in one declaration. u32 and a pointer are
> >> quite different types.
> >
> > I'm not sure to understand.
> > LVTS_INT_SENSORx are not pointers but register values.
> >
>
> u32 mask[] is a pointer to a array of u32 values of undefined length.
>
I will change this to :
u32 value;
u32 masks[] = {
> [...]
>
> >>> +static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
> >>
> >> You did not test it, right? Build with section mismatch analysis...
> >
> > I'm not sure to fully understand this comment.
> > Would you explain, please?
> >
>
> AFAIU:
>
> lvts_golden_temp_init() and lvts_ctrl_init() are called from a function that is
> not in __init section:
> lvts_domain_init()
>
> So if you free up the first to functions after init but not the callers, things
> can explote.
>
__init will be removed for all functions.
> [...]
>
> >>> +
> >>> +static int lvts_probe(struct platform_device *pdev)
> >>> +{
> >>> + struct lvts_data *lvts_data;
> >>> + struct lvts_domain *lvts_td;
> >>> + struct device *dev = &pdev->dev;
> >>> + struct resource *res;
> >>> + int irq, ret;
> >>> +
> >>> + lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL);
> >>> + if (!lvts_td)
> >>> + return -ENOMEM;
> >>> +
> >>> + lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
> >>
> >> Why do you need case?
> >
> > Would you explain, please?
> >
>
> Typo by Krysztof, he meant the cast.
> lvts_data = of_device_get_match_data(dev);
> should be good enough.
>
OK,
It will be like you suggested.
> Regards,
> Matthias
Thank you for the review!
Best regards,
Balsam
Hi Krzysztof,
On Wed, Feb 1, 2023 at 6:12 PM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 01/02/2023 17:46, Balsam CHIHI wrote:
> >>> +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
> >>> +
> >>> +static struct dentry *root;
> >>
> >> How do you handle two instances of driver?
> >
> > This root node is the topmost directory for debugfs called 'lvts', the
> > different driver instances are below this. It is a singleton.
>
> Indeed. What about removal? Aren't you remobing entire directory
> structure on first device removal?
>
For now, the driver only supports one instance.
I will find a way to handle this when the driver supports more instances.
Is this suggestion OK for you?
> (...)
>
> >>> +
> >>> + of_property_for_each_string(np, "nvmem-cell-names", prop, cell_name) {
> >>> + size_t len;
> >>> + u8 *efuse;
> >>> +
> >>> + cell = of_nvmem_cell_get(np, cell_name);
> >>> + if (IS_ERR(cell)) {
> >>> + dev_dbg(dev, "Failed to get cell '%s'\n", cell_name);
> >>
> >> Is this an error? If so, why debug? dbg is not for errors.
> >
> > AFAIK using dev_dbg does not increase ELF size when DEBUG is disabled.
> > If this is not a good reason for you, then I will change it to dev_err.
>
> But also dev_dbg are not visible in error or warn level logs. If this is
> not an error, then indeed dev_dbg could be fine. But errors should be
> verbose.
OK,
I will replace all "dev_dbg" with "dev_err" in this function.
>
> >
> >>
> >>> + return PTR_ERR(cell);
> >>> + }
> >>> +
> >>> + efuse = nvmem_cell_read(cell, &len);
> >>> +
> >>> + nvmem_cell_put(cell);
> >>> +
> >>> + if (IS_ERR(efuse)) {
> >>> + dev_dbg(dev, "Failed to read cell '%s'\n", cell_name);
> >>> + return PTR_ERR(efuse);
> >>> + }
> >>> +
> >>> + lvts_td->calib = devm_krealloc(dev, lvts_td->calib,
> >>> + lvts_td->calib_len + len, GFP_KERNEL);
> >>> + if (!lvts_td->calib)
> >>> + return -ENOMEM;
> >>> +
> >>> + memcpy(lvts_td->calib + lvts_td->calib_len, efuse, len);
> >>> +
> >>> + lvts_td->calib_len += len;
> >>> +
> >>> + kfree(efuse);
> >>> + }
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int __init lvts_golden_temp_init(struct device *dev, u32 *value)
> >>
> >> You did not test it, right? Build with section mismatch analysis...
> >
> > I'm not sure to fully understand this comment.
> > Would you explain, please?
>
> git grep -i "section mismatch" leads to lib/Kconfig.debug and
> DEBUG_SECTION_MISMATCH
__init is removed from all functions.
>
> (...)
>
> >>> +static struct lvts_ctrl_data mt8195_lvts_data_ctrl[] = {
> >>
> >> Why this cannot be const?
> >
> > I've got the following warning when I added "const"
> > drivers/thermal/mediatek/lvts_thermal.c:1286:27: warning:
> > initialization discards ‘const’ qualifier from pointer target type
> > [-Wdiscarded-qualifiers]
> > 1286 | .lvts_ctrl = mt8195_lvts_data_ctrl,
> > | ^~~~~~~~~~~~~~~~~~~~~~~~~
>
> As with every const... Do you need lvts_ctrl to be non-const? If yes,
> then how do you handle multiple devices (singleton)?
>
I found a fix for this it was simple.
add const here as you suggested
and in other function parameters every time we use this variable.
> Best regards,
> Krzysztof
>
Thank you for the review!
Best regards,
Balsam
On Wed, Feb 01, 2023 at 08:55:07AM +0100, Krzysztof Kozlowski wrote:
> On 31/01/2023 16:38, [email protected] wrote:
> > From: Balsam CHIHI <[email protected]>
> >
> > The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
> > controllers contained in a thermal domain.
> >
> > A thermal domains can be the MCU or the AP.
> >
> > Each thermal domains contain up to seven controllers, each thermal
> > controller handle up to four thermal sensors.
> >
> > The LVTS has two Finite State Machines (FSM), one to handle the
> > functionin temperatures range like hot or cold temperature and another
> > one to handle monitoring trip point. The FSM notifies via interrupts
> > when a trip point is crossed.
> >
>
> (...)
>
> > +
> > +struct lvts_domain {
> > + struct lvts_ctrl *lvts_ctrl;
> > + struct reset_control *reset;
> > + struct clk *clk;
> > + int num_lvts_ctrl;
> > + void __iomem *base;
> > + size_t calib_len;
> > + u8 *calib;
> > +};
> > +
> > +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
> > +
> > +static struct dentry *root;
>
> How do you handle two instances of driver?
For now, we can put the entry in /sys/kernel/debug/<dev_name>
I'm preparing a debugfs series for the thermal framework and that will provide
an entry to hook in for the sensors debugfs if available
On 06/02/2023 15:07, Daniel Lezcano wrote:
> On Wed, Feb 01, 2023 at 08:55:07AM +0100, Krzysztof Kozlowski wrote:
>> On 31/01/2023 16:38, [email protected] wrote:
>>> From: Balsam CHIHI <[email protected]>
>>>
>>> The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
>>> controllers contained in a thermal domain.
>>>
>>> A thermal domains can be the MCU or the AP.
>>>
>>> Each thermal domains contain up to seven controllers, each thermal
>>> controller handle up to four thermal sensors.
>>>
>>> The LVTS has two Finite State Machines (FSM), one to handle the
>>> functionin temperatures range like hot or cold temperature and another
>>> one to handle monitoring trip point. The FSM notifies via interrupts
>>> when a trip point is crossed.
>>>
>>
>> (...)
>>
>>> +
>>> +struct lvts_domain {
>>> + struct lvts_ctrl *lvts_ctrl;
>>> + struct reset_control *reset;
>>> + struct clk *clk;
>>> + int num_lvts_ctrl;
>>> + void __iomem *base;
>>> + size_t calib_len;
>>> + u8 *calib;
>>> +};
>>> +
>>> +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
>>> +
>>> +static struct dentry *root;
>>
>> How do you handle two instances of driver?
>
> For now, we can put the entry in /sys/kernel/debug/<dev_name>
>
> I'm preparing a debugfs series for the thermal framework and that will provide
> an entry to hook in for the sensors debugfs if available
The code looked like it does not support it at all. I think it would
remove entries from second instance, while unbinding the first (or the
opposite).
Best regards,
Krzysztof
Hi Daniel,
On Mon, Feb 6, 2023 at 3:07 PM Daniel Lezcano <[email protected]> wrote:
>
> On Wed, Feb 01, 2023 at 08:55:07AM +0100, Krzysztof Kozlowski wrote:
> > On 31/01/2023 16:38, [email protected] wrote:
> > > From: Balsam CHIHI <[email protected]>
> > >
> > > The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
> > > controllers contained in a thermal domain.
> > >
> > > A thermal domains can be the MCU or the AP.
> > >
> > > Each thermal domains contain up to seven controllers, each thermal
> > > controller handle up to four thermal sensors.
> > >
> > > The LVTS has two Finite State Machines (FSM), one to handle the
> > > functionin temperatures range like hot or cold temperature and another
> > > one to handle monitoring trip point. The FSM notifies via interrupts
> > > when a trip point is crossed.
> > >
> >
> > (...)
> >
> > > +
> > > +struct lvts_domain {
> > > + struct lvts_ctrl *lvts_ctrl;
> > > + struct reset_control *reset;
> > > + struct clk *clk;
> > > + int num_lvts_ctrl;
> > > + void __iomem *base;
> > > + size_t calib_len;
> > > + u8 *calib;
> > > +};
> > > +
> > > +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
> > > +
> > > +static struct dentry *root;
> >
> > How do you handle two instances of driver?
>
> For now, we can put the entry in /sys/kernel/debug/<dev_name>
Yes, sure.
I will do the necessary changes.
>
> I'm preparing a debugfs series for the thermal framework and that will provide
> an entry to hook in for the sensors debugfs if available
It would be a good feature!
good luck!
Best regards,
Balsam.
On 06/02/2023 15:30, Krzysztof Kozlowski wrote:
> On 06/02/2023 15:07, Daniel Lezcano wrote:
>> On Wed, Feb 01, 2023 at 08:55:07AM +0100, Krzysztof Kozlowski wrote:
>>> On 31/01/2023 16:38, [email protected] wrote:
>>>> From: Balsam CHIHI <[email protected]>
>>>>
>>>> The Low Voltage Thermal Sensor (LVTS) is a multiple sensors, multi
>>>> controllers contained in a thermal domain.
>>>>
>>>> A thermal domains can be the MCU or the AP.
>>>>
>>>> Each thermal domains contain up to seven controllers, each thermal
>>>> controller handle up to four thermal sensors.
>>>>
>>>> The LVTS has two Finite State Machines (FSM), one to handle the
>>>> functionin temperatures range like hot or cold temperature and another
>>>> one to handle monitoring trip point. The FSM notifies via interrupts
>>>> when a trip point is crossed.
>>>>
>>>
>>> (...)
>>>
>>>> +
>>>> +struct lvts_domain {
>>>> + struct lvts_ctrl *lvts_ctrl;
>>>> + struct reset_control *reset;
>>>> + struct clk *clk;
>>>> + int num_lvts_ctrl;
>>>> + void __iomem *base;
>>>> + size_t calib_len;
>>>> + u8 *calib;
>>>> +};
>>>> +
>>>> +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS
>>>> +
>>>> +static struct dentry *root;
>>>
>>> How do you handle two instances of driver?
>>
>> For now, we can put the entry in /sys/kernel/debug/<dev_name>
>>
>> I'm preparing a debugfs series for the thermal framework and that will provide
>> an entry to hook in for the sensors debugfs if available
>
> The code looked like it does not support it at all. I think it would
> remove entries from second instance, while unbinding the first (or the
> opposite).
Yes, and you are right.
So instead of having:
/sys/kernel/debugfs/lvts/lvts@mcu
We will have:
/sys/kernel/debugfs/lvts@mcu
Later, when the other domain is added, we will have:
/sys/kernel/debugfs/lvts@mcu
/sys/kernel/debugfs/lvts@ap
Each instance takes care of removing its own entry
When I'll introduce thermal debugfs, I'll take care of massaging the
driver to have them like:
/sys/kernel/debugfs/thermal/thermal_zone/0/sensors/lvts@mcu,0
/sys/kernel/debugfs/thermal/thermal_zone/1/sensors/lvts@ap,1
--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
From: Balsam CHIHI <[email protected]>
Replace memcpy 2 bytes by sizeof(int) bytes of LVTS calibration data.
Reported-by: Dan Carpenter <[email protected]>
Signed-off-by: Balsam CHIHI <[email protected]>
---
Rebased on top of thermal/linux-next
base-commit: 6828e402d06f7c574430b61c05db784cd847b19f
Original email :
Hello Balsam CHIHI,
The patch f5f633b18234: "thermal/drivers/mediatek: Add the Low
Voltage Thermal Sensor driver" from Feb 9, 2023, leads to the
following Smatch static checker warning:
drivers/thermal/mediatek/lvts_thermal.c:562 lvts_calibration_init()
warn: not copying enough bytes for '&lvts_ctrl->calibration[i]' (4 vs 2 bytes)
drivers/thermal/mediatek/lvts_thermal.c
555 static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl,
556 const struct lvts_ctrl_data *lvts_ctrl_data,
557 u8 *efuse_calibration)
558 {
559 int i;
560
561 for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++)
--> 562 memcpy(&lvts_ctrl->calibration[i],
563 efuse_calibration + lvts_ctrl_data->cal_offset[i], 2);
^
This is copying an array of known ints to a u32 array. It should copy
sizeof(int) instead of 2. It only works because the data you're on
little endian and the data is small.
564
565 return 0;
566 }
regards,
dan carpenter
---
---
drivers/thermal/mediatek/lvts_thermal.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c
index ddfdcbcf6d86..b505c6b49031 100644
--- a/drivers/thermal/mediatek/lvts_thermal.c
+++ b/drivers/thermal/mediatek/lvts_thermal.c
@@ -575,7 +575,7 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl
for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++)
memcpy(&lvts_ctrl->calibration[i],
- efuse_calibration + lvts_ctrl_data->cal_offset[i], 2);
+ efuse_calibration + lvts_ctrl_data->cal_offset[i], sizeof(int));
return 0;
}
base-commit: 6828e402d06f7c574430b61c05db784cd847b19f
prerequisite-patch-id: 73be949bd16979769e5b94905b244dcee4a8f687
prerequisite-patch-id: d23d83a946e5b876ef01a717fd51b07df1fa08dd
prerequisite-patch-id: d67f2455eef1c4a9ecc460dbf3c2e3ad47d213ec
prerequisite-patch-id: 9076e9b3bd3cc411b7b80344211364db5f0cca17
prerequisite-patch-id: e220d6ae26786f524c249588433f02e5f5f906ad
prerequisite-patch-id: b407d2998e57678952128b3a4bac92a379132b09
prerequisite-patch-id: fbb9212ce8c3530da17d213f56fa334ce4fa1b2b
prerequisite-patch-id: 5db9eed2659028cf4419f2de3d093af7df6c2dad
prerequisite-patch-id: a83c00c628605d1c8fbe1d97074f9f28efb1bcfc
prerequisite-patch-id: 56a245620a4f8238cf1ba3844dc348de3db33845
prerequisite-patch-id: 7df24b0bf11129ddd3356eacf192cc3fdb2bcded
prerequisite-patch-id: 3213ca70cb5b26d54a7137ff40ca8cd2a795c414
prerequisite-patch-id: 6c2202e85215d1c7e8ab16a6b85922e994c68d9b
--
2.34.1
Il 07/03/23 14:42, [email protected] ha scritto:
> From: Balsam CHIHI <[email protected]>
>
> Replace memcpy 2 bytes by sizeof(int) bytes of LVTS calibration data.
sizeof(int) is architecture dependant... please use a fixed size type instead.
Also, shouldn't this be u16?!
Regards,
Angelo
On Wed, Mar 08, 2023 at 10:10:34AM +0100, AngeloGioacchino Del Regno wrote:
> Il 07/03/23 14:42, [email protected] ha scritto:
> > From: Balsam CHIHI <[email protected]>
> >
> > Replace memcpy 2 bytes by sizeof(int) bytes of LVTS calibration data.
>
> sizeof(int) is architecture dependant...
>
On Linux sizeof(int) is always 4.
I'm just so confused what you are talking about. Are you thinking about
sizeof(long)? Are you thinking about CPUs from the 1970s? Linux wasn't
invented until the 90s so the old CPUs were already in museums at that
point.
> please use a fixed size type instead.
This is an unusual style opinion that I have not heard before.
Hopefully, you just got ints and longs confused so we can move on
without discussing it too much. We're copying an int so sizeof(int) is
obviously correct. It's hard to know how to respond.
> Also, shouldn't this be u16?!
What? Why would you think that?
regards,
dan carpenter