2022-08-04 13:53:53

by Balsam CHIHI

[permalink] [raw]
Subject: [PATCH v8.1, 0/7] Add LVTS architecture thermal

From: Balsam CHIHI <[email protected]>

This series moves thermal files related to MediaTek to the mediatek folder.
And introduce the new architecture LVTS (low voltage thermal sensor) driver to report
the highest temperature in the SoC and record the highest temperature sensor,
each sensor as a hot zone.
The LVTS body is divided into two parts, the LVTS controller and the LVTS device.
The LVTS controller can connect up to 4 LVTS devices, and each LVTS device
can connect up to 7 TSMCUs.

The architecture will be the first to be used on mt8192 and mt8195.

Changelog:
Changes in v8.1 :
- 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

Changes in 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:
- https://patchwork.kernel.org/project/linux-mediatek/list/?series=637849
- https://patchwork.kernel.org/project/linux-pm/list/?series=639386

Changes in 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

Changes in v5 :
- Use 'git mv' for the relocated file.

Changes in v4 :
- Rebase to kernel-v5.13-rc1
- Resend

Changes in v3 :
- change the expression in the lvts_temp_to_raw to dev_s64.

Changes in 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.

Alexandre Bailon (2):
dt-bindings: thermal: Add binding document for LVTS thermal
controllers
arm64: dts: mt8195: Add efuse node to mt8195

Balsam CHIHI (1):
arm64: dts: mt8192: Add thermal zone

Michael Kao (3):
thermal: mediatek: Relocate driver to mediatek folder
thermal: mediatek: Add LVTS driver for mt8192 thermal zones
thermal: mediatek: Add thermal zone settings for mt8195

Tinghan Shen (1):
arm64: dts: mt8195: Add thermal zone

.../thermal/mediatek,lvts-thermal.yaml | 77 ++
arch/arm64/boot/dts/mediatek/mt8192.dtsi | 113 ++-
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 125 ++-
drivers/thermal/Kconfig | 14 +-
drivers/thermal/Makefile | 2 +-
drivers/thermal/mediatek/Kconfig | 47 +
drivers/thermal/mediatek/Makefile | 3 +
drivers/thermal/mediatek/lvts_thermal.c | 855 ++++++++++++++++++
drivers/thermal/mediatek/lvts_thermal.h | 377 ++++++++
drivers/thermal/mediatek/lvts_v4.c | 464 ++++++++++
.../mtxxxx_thermal.c} | 2 +-
11 files changed, 2065 insertions(+), 14 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
create mode 100644 drivers/thermal/mediatek/lvts_thermal.c
create mode 100644 drivers/thermal/mediatek/lvts_thermal.h
create mode 100644 drivers/thermal/mediatek/lvts_v4.c
rename drivers/thermal/{mtk_thermal.c => mediatek/mtxxxx_thermal.c} (99%)

--
2.34.1



2022-08-04 13:58:49

by Balsam CHIHI

[permalink] [raw]
Subject: [PATCH v8.1, 4/7] thermal: mediatek: Add LVTS driver for mt8192 thermal zones

From: Michael Kao <[email protected]>

Add a LVTS V4 (Low Voltage Thermal Sensor) driver to report junction
temperatures in MediaTek SoC mt8192 and register the maximum temperature
of sensors and each sensor as a thermal zone.

Signed-off-by: Yu-Chia Chang <[email protected]>
Signed-off-by: Michael Kao <[email protected]>
Signed-off-by: Ben Tseng <[email protected]>
Signed-off-by: Alexandre Bailon <[email protected]>
Signed-off-by: Balsam CHIHI <[email protected]>
---
drivers/thermal/mediatek/Kconfig | 24 +
drivers/thermal/mediatek/Makefile | 2 +
drivers/thermal/mediatek/lvts_thermal.c | 855 ++++++++++++++++++++++++
drivers/thermal/mediatek/lvts_thermal.h | 376 +++++++++++
drivers/thermal/mediatek/lvts_v4.c | 241 +++++++
5 files changed, 1498 insertions(+)
create mode 100644 drivers/thermal/mediatek/lvts_thermal.c
create mode 100644 drivers/thermal/mediatek/lvts_thermal.h
create mode 100644 drivers/thermal/mediatek/lvts_v4.c

diff --git a/drivers/thermal/mediatek/Kconfig b/drivers/thermal/mediatek/Kconfig
index 592c849b9fed..afef43a0e7ca 100644
--- a/drivers/thermal/mediatek/Kconfig
+++ b/drivers/thermal/mediatek/Kconfig
@@ -20,4 +20,28 @@ config MTK_SOC_THERMAL
configures thermal controllers to collect temperature
via AUXADC interface.

+config MTK_LVTS_THERMAL
+ tristate "LVTS (Low Voltage Thermal Sensor) driver for MediaTek SoCs"
+ depends on HAS_IOMEM
+ depends on NVMEM
+ depends on RESET_CONTROLLER
+ help
+ Enable this option if you want to get SoC temperature
+ information for MediaTek platforms. This driver configures
+ LVTS (Low Voltage Thermal Sensor) thermal controllers to
+ collect temperatures via ASIF (Analog Serial Interface).
+
+if MTK_LVTS_THERMAL
+
+config MTK_LVTS_V4
+ tristate "LVTS V4 Thermal Driver for MediaTek SoCs"
+ depends on HAS_IOMEM
+ depends on NVMEM
+ depends on RESET_CONTROLLER
+ help
+ Enable this option if you want to get SoC temperature
+ information for LVTS V4.
+
+endif
+
endif
diff --git a/drivers/thermal/mediatek/Makefile b/drivers/thermal/mediatek/Makefile
index c2b9a166fbef..80a14be07053 100644
--- a/drivers/thermal/mediatek/Makefile
+++ b/drivers/thermal/mediatek/Makefile
@@ -1 +1,3 @@
obj-$(CONFIG_MTK_SOC_THERMAL) += mtxxxx_thermal.o
+obj-$(CONFIG_MTK_LVTS_THERMAL) += lvts_thermal.o
+obj-$(CONFIG_MTK_LVTS_V4) += lvts_v4.o
diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c
new file mode 100644
index 000000000000..77297a91b9fa
--- /dev/null
+++ b/drivers/thermal/mediatek/lvts_thermal.c
@@ -0,0 +1,855 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/thermal.h>
+#include "lvts_thermal.h"
+
+static int lvts_raw_to_temp(struct lvts_formula_coeff *co, unsigned int msr_raw)
+{
+ /* This function returns degree mC */
+
+ int temp;
+
+ temp = (co->a * ((unsigned long long)msr_raw)) >> 14;
+ temp = temp + co->golden_temp * 500 + co->b;
+
+ return temp;
+}
+
+static unsigned int lvts_temp_to_raw(struct lvts_formula_coeff *co, int temp)
+{
+ unsigned int msr_raw;
+
+ msr_raw = div_s64((s64)((co->golden_temp * 500 + co->b - temp)) << 14, (-1 * co->a));
+
+ return msr_raw;
+}
+
+static int soc_temp_lvts_read_temp(void *data, int *temperature)
+{
+ struct soc_temp_tz *lvts_tz = (struct soc_temp_tz *)data;
+ struct lvts_data *lvts_data = lvts_tz->lvts_data;
+ struct device *dev = lvts_data->dev;
+ unsigned int msr_raw;
+
+ msr_raw = readl(lvts_data->reg[lvts_tz->id]) & MRS_RAW_MASK;
+ if (msr_raw == 0) {
+ /* Prevents a false critical temperature trap */
+ *temperature = 0;
+ dev_dbg(dev, "LVTS not yet ready\n");
+
+ } else
+ *temperature = lvts_raw_to_temp(&lvts_data->coeff, msr_raw);
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops soc_temp_lvts_ops = {
+ .get_temp = soc_temp_lvts_read_temp,
+};
+
+static void lvts_write_device(struct lvts_data *lvts_data, unsigned int data, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+
+ writel(DEVICE_WRITE | data, LVTS_CONFIG_0 + base);
+ usleep_range(20, 30);
+}
+
+static unsigned int lvts_read_device(struct lvts_data *lvts_data, unsigned int reg_idx, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ struct device *dev = lvts_data->dev;
+ unsigned int data;
+ int ret;
+
+ writel(READ_DEVICE_REG(reg_idx), LVTS_CONFIG_0 + base);
+ usleep_range(20, 30);
+ ret = readl_poll_timeout(LVTS_CONFIG_0 + base, data, !(data & DEVICE_ACCESS_STARTUS), 2, 200);
+ if (ret)
+ dev_err(dev, "LVTS_TC_%d DEVICE_ACCESS_START is not ready\n", tc_id);
+
+ data = readl(LVTSRDATA0_0 + base);
+
+ return data;
+}
+
+static const char * const lvts_error_table[] = {
+ "Idle",
+ "Write transaction",
+ "Waiting for read after write",
+ "Disable continue fetching on device",
+ "Read transaction",
+ "Set device special register for voltage threshold",
+ "Set TSMCU number for fetch"
+};
+
+static void wait_all_tc_sensing_point_idle(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ struct device *dev = lvts_data->dev;
+ unsigned int error_code, is_error;
+ int i, cnt, ret;
+
+ for (cnt = 0; cnt < 2; cnt++) {
+ is_error = 0;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ ret = readl_poll_timeout(LVTSMSRCTL1_0 + base, error_code,
+ !(error_code & ALL_TC_SENSING_POINT_STATUS), 2, 200);
+ error_code = ((error_code & TC_SENSING_POINT_10) >> 8) +
+ ((error_code & TC_SENSING_POINT_7) >> 6) + (error_code & TC_SENSING_POINT_0);
+ if (ret)
+ dev_err(dev, "LVTS_TC_%d Error Code : %s\n", i, lvts_error_table[error_code]);
+
+ if (error_code != 0)
+ is_error = 1;
+ }
+
+ if (is_error == 0)
+ break;
+ }
+}
+
+static void lvts_reset(struct lvts_data *lvts_data)
+{
+ if (lvts_data->reset)
+ reset_control_assert(lvts_data->reset);
+
+ if (lvts_data->reset)
+ reset_control_deassert(lvts_data->reset);
+}
+
+static void device_identification(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ struct device *dev = lvts_data->dev;
+ unsigned int i, data;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ writel(ENABLE_LVTS_CTRL_CLK, LVTSCLKEN_0 + base);
+ lvts_write_device(lvts_data, RESET_ALL_DEVICES, i);
+ writel(READ_BACK_DEVICE_ID, LVTS_CONFIG_0 + base);
+ usleep_range(20, 30);
+ /* Check LVTS device ID */
+ data = (readl(LVTS_ID_0 + base) & DEVICE_REG_DATA);
+ if (data != (lvts_data->tc->dev_id + i))
+ dev_err(dev, "LVTS_TC_%d, Device ID should be 0x%x, but 0x%x\n",
+ i, (lvts_data->tc->dev_id + i), data);
+ }
+}
+
+static void disable_all_sensing_points(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ unsigned int i;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ writel(DISABLE_SENSING_POINT, LVTSMONCTL0_0 + base);
+ }
+}
+
+static void enable_all_sensing_points(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ struct device *dev = lvts_data->dev;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ unsigned int i, num;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ num = tc[i].num_sensor;
+ if (num > ALL_SENSING_POINTS) {
+ dev_err(dev, "LVTS_TC_%d, illegal number of sensors: %d\n", i, tc[i].num_sensor);
+
+ continue;
+ }
+
+ writel(ENABLE_SENSING_POINT(num), LVTSMONCTL0_0 + base);
+ }
+}
+
+static void set_polling_speed(struct lvts_data *lvts_data, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ struct device *dev = lvts_data->dev;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ unsigned int lvts_mon_ctl_1, lvts_mon_ctl_2;
+
+ lvts_mon_ctl_1 = ((tc[tc_id].tc_speed->group_interval_delay << 20) & GROUP_INTERVAL_DELAY_MASK) |
+ (tc[tc_id].tc_speed->period_unit & PERIOD_UNIT_MASK);
+ lvts_mon_ctl_2 = ((tc[tc_id].tc_speed->filter_interval_delay << 16) & FILTER_INTERVAL_DELAY_MASK) |
+ (tc[tc_id].tc_speed->sensor_interval_delay & SENSOR_INTERVAL_DELAY_MASK);
+ /*
+ * Clock source of LVTS thermal controller is 26MHz.
+ * Period unit is a base for all interval delays
+ * All interval delays must multiply it to convert a setting to time.
+ * Filter interval delay is a delay between two samples of the same sensor
+ * Sensor interval delay is a delay between two samples of differnet 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 = 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
+ */
+ writel(lvts_mon_ctl_1, LVTSMONCTL1_0 + base);
+ writel(lvts_mon_ctl_2, LVTSMONCTL2_0 + base);
+ dev_dbg(dev, "lvts_tc_%d, LVTSMONCTL1_0= 0x%x, LVTSMONCTL2_0= 0x%x\n",
+ tc_id, readl(LVTSMONCTL1_0 + base), readl(LVTSMONCTL2_0 + base));
+}
+
+static void set_hw_filter(struct lvts_data *lvts_data, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ struct device *dev = lvts_data->dev;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ unsigned int option = tc[tc_id].hw_filter & 0x7;
+
+ /*
+ * hw filter
+ * 000: Get one sample
+ * 001: Get 2 samples and average them
+ * 010: Get 4 samples, drop max and min, then average the rest of 2 samples
+ * 011: Get 6 samples, drop max and min, then average the rest of 4 samples
+ * 100: Get 10 samples, drop max and min, then average the rest of 8 samples
+ * 101: Get 18 samples, drop max and min, then average the rest of 16 samples
+ */
+ option = (option << 9) | (option << 6) | (option << 3) | option;
+ writel(option, LVTSMSRCTL0_0 + base);
+ dev_dbg(dev, "lvts_tc_%d, LVTSMSRCTL0_0= 0x%x\n", tc_id, readl(LVTSMSRCTL0_0 + base));
+}
+
+static int get_dominator_index(struct lvts_data *lvts_data, int tc_id)
+{
+ struct device *dev = lvts_data->dev;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ int d_index;
+
+ if (tc[tc_id].dominator_sensing_point == ALL_SENSING_POINTS) {
+ d_index = ALL_SENSING_POINTS;
+ } else if (tc[tc_id].dominator_sensing_point < tc[tc_id].num_sensor) {
+ d_index = tc[tc_id].dominator_sensing_point;
+ } else {
+ dev_err(dev, "LVTS_TC_%d: dominator sensing point = %d. \
+ It should be smaller than num_sensor %d\n",
+ tc_id, tc[tc_id].dominator_sensing_point, tc[tc_id].num_sensor);
+ dev_err(dev, "Using the sensing point 0 as the dominated sensor\n");
+ d_index = SENSING_POINT0;
+ }
+
+ return d_index;
+}
+
+static void disable_hw_reboot_interrupt(struct lvts_data *lvts_data, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ unsigned int temp;
+
+ /*
+ * LVTS thermal controller has two interrupts for thermal HW reboot.
+ * One is for AP SW and the other is for RGU.
+ * The interrupt of AP SW can be turned off by a bit of a register,
+ * but the other for RGU cannot.
+ * To prevent rebooting device accidentally, we are going to add
+ * a huge offset 0x3FFF to LVTS and make it always report extremely low temperature.
+ * LVTS always adds the offset 0x3FFF to MSR_RAW.
+ * When MSR_RAW is larger, SW will convert lower temperature.
+ */
+ temp = readl(LVTSPROTCTL_0 + base);
+ writel(temp | 0x3FFF, LVTSPROTCTL_0 + base);
+
+ /* Disable the interrupt of AP SW */
+ temp = readl(LVTSMONINT_0 + base);
+ writel(temp & ~(STAGE3_INT_EN), LVTSMONINT_0 + base);
+}
+
+static void enable_hw_reboot_interrupt(struct lvts_data *lvts_data, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ unsigned int temp;
+
+ /* Enable the interrupt of AP SW */
+ temp = readl(LVTSMONINT_0 + base);
+ writel(temp | STAGE3_INT_EN, LVTSMONINT_0 + base);
+
+ /* Clear the offset */
+ temp = readl(LVTSPROTCTL_0 + base);
+ writel(temp & ~PROTOFFSET, LVTSPROTCTL_0 + base);
+}
+
+static void set_tc_hw_reboot_threshold(struct lvts_data *lvts_data, int trip_point, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ struct device *dev = lvts_data->dev;
+ unsigned int msr_raw, temp, config, d_index;
+
+ d_index = get_dominator_index(lvts_data, tc_id);
+
+ dev_dbg(dev, "lvts_tc_%d: dominator sensing point = %d\n", tc_id, d_index);
+
+ disable_hw_reboot_interrupt(lvts_data, tc_id);
+ temp = readl(LVTSPROTCTL_0 + base);
+ if (d_index == ALL_SENSING_POINTS) {
+ /* Maximum of 4 sensing points */
+ config = (0x1 << 16);
+ writel(config | temp, LVTSPROTCTL_0 + base);
+
+ } else {
+ /* Select protection sensor */
+ config = ((d_index << 2) + 0x2) << 16;
+ writel(config | temp, LVTSPROTCTL_0 + base);
+ }
+
+ msr_raw = lvts_temp_to_raw(&lvts_data->coeff, trip_point);
+ writel(msr_raw, LVTSPROTTC_0 + base);
+ enable_hw_reboot_interrupt(lvts_data, tc_id);
+}
+
+static void set_all_tc_hw_reboot(struct lvts_data *lvts_data)
+{
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ int i, trip_point;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ trip_point = tc[i].hw_reboot_trip_point;
+
+ if (tc[i].num_sensor == 0)
+ continue;
+
+ if (trip_point == THERMAL_TEMP_INVALID)
+ continue;
+
+ set_tc_hw_reboot_threshold(lvts_data, trip_point, i);
+ }
+}
+
+static int lvts_init(struct lvts_data *lvts_data)
+{
+ struct platform_ops *ops = &lvts_data->ops;
+ struct device *dev = lvts_data->dev;
+ int ret;
+
+ ret = clk_prepare_enable(lvts_data->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable lvts controller clock: %d\n", ret);
+
+ return ret;
+ }
+
+ lvts_reset(lvts_data);
+ device_identification(lvts_data);
+ if (ops->device_enable_and_init)
+ ops->device_enable_and_init(lvts_data);
+
+ if (HAS_FEATURE(lvts_data, FEATURE_DEVICE_AUTO_RCK) && (ops->device_enable_auto_rck))
+ ops->device_enable_auto_rck(lvts_data);
+
+ else if (ops->device_read_count_rc_n)
+ ops->device_read_count_rc_n(lvts_data);
+
+ if (ops->set_cal_data)
+ ops->set_cal_data(lvts_data);
+
+ disable_all_sensing_points(lvts_data);
+ wait_all_tc_sensing_point_idle(lvts_data);
+ if (ops->init_controller)
+ ops->init_controller(lvts_data);
+
+ enable_all_sensing_points(lvts_data);
+ set_all_tc_hw_reboot(lvts_data);
+
+ return 0;
+}
+
+static int prepare_calibration_data(struct lvts_data *lvts_data)
+{
+ struct device *dev = lvts_data->dev;
+ struct lvts_sensor_cal_data *cal_data = &lvts_data->cal_data;
+ struct platform_ops *ops = &lvts_data->ops;
+ int i, offset;
+ char buffer[512];
+
+ cal_data->count_r = devm_kcalloc(dev, lvts_data->num_sensor,
+ sizeof(*cal_data->count_r), GFP_KERNEL);
+ if (!cal_data->count_r)
+ return -ENOMEM;
+
+ cal_data->count_rc = devm_kcalloc(dev, lvts_data->num_sensor,
+ sizeof(*cal_data->count_rc), GFP_KERNEL);
+ if (!cal_data->count_rc)
+ return -ENOMEM;
+
+ if (ops->efuse_to_cal_data && !cal_data->use_fake_efuse)
+ ops->efuse_to_cal_data(lvts_data);
+ if (cal_data->golden_temp == 0 || cal_data->golden_temp > GOLDEN_TEMP_MAX)
+ cal_data->use_fake_efuse = 1;
+
+ if (cal_data->use_fake_efuse) {
+ /* It means all efuse data are equal to 0 */
+ dev_err(dev, "This sample is not calibrated, fake !!\n");
+ cal_data->golden_temp = cal_data->default_golden_temp;
+ for (i = 0; i < lvts_data->num_sensor; i++) {
+ cal_data->count_r[i] = cal_data->default_count_r;
+ cal_data->count_rc[i] = cal_data->default_count_rc;
+ }
+ }
+
+ lvts_data->coeff.golden_temp = cal_data->golden_temp;
+ dev_dbg(dev, "golden_temp = %d\n", cal_data->golden_temp);
+ offset = snprintf(buffer, sizeof(buffer), "[lvts_cal] num:g_count:g_count_rc ");
+ for (i = 0; i < lvts_data->num_sensor; i++)
+ offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%d:%d:%d ",
+ i, cal_data->count_r[i], cal_data->count_rc[i]);
+
+ return 0;
+}
+
+static int get_calibration_data(struct lvts_data *lvts_data)
+{
+ struct device *dev = lvts_data->dev;
+ char cell_name[32];
+ struct nvmem_cell *cell;
+ u32 *buf;
+ size_t len;
+ int i, j, index = 0, ret;
+
+ lvts_data->efuse = devm_kcalloc(dev, lvts_data->num_efuse_addr,
+ sizeof(*lvts_data->efuse), GFP_KERNEL);
+ if (!lvts_data->efuse)
+ return -ENOMEM;
+
+ for (i = 0; i < lvts_data->num_efuse_block; i++) {
+ snprintf(cell_name, sizeof(cell_name), "lvts_calib_data%d", i + 1);
+ cell = nvmem_cell_get(dev, cell_name);
+ if (IS_ERR(cell)) {
+ dev_err(dev, "Failed to get nvmem cell %s\n", cell_name);
+
+ return PTR_ERR(cell);
+ }
+
+ buf = (u32 *)nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ for (j = 0; j < (len / sizeof(u32)); j++) {
+ if (index >= lvts_data->num_efuse_addr) {
+ dev_err(dev, "Array efuse is going to overflow");
+ kfree(buf);
+
+ return -EINVAL;
+ }
+
+ lvts_data->efuse[index] = buf[j];
+ index++;
+ }
+
+ kfree(buf);
+ }
+
+ ret = prepare_calibration_data(lvts_data);
+
+ return ret;
+}
+
+static int lvts_init_tc_regs(struct device *dev, struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ unsigned int i, j, s_index;
+
+ lvts_data->reg = devm_kcalloc(dev, lvts_data->num_sensor,
+ sizeof(*lvts_data->reg), GFP_KERNEL);
+ if (!lvts_data->reg)
+ return -ENOMEM;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ for (j = 0; j < tc[i].num_sensor; j++) {
+ s_index = tc[i].sensor_map[j];
+ lvts_data->reg[s_index] = LVTSMSR0_0 + base + 0x4 * j;
+ }
+ }
+
+ return 0;
+}
+
+static int of_update_lvts_data(struct lvts_data *lvts_data, struct platform_device *pdev)
+{
+ struct device *dev = lvts_data->dev;
+ struct resource *res;
+ int ret;
+
+ lvts_data->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(lvts_data->clk))
+ return PTR_ERR(lvts_data->clk);
+
+ /* Get base address */
+ res = platform_get_mem_or_io(pdev, 0);
+ if (!res) {
+ dev_err(dev, "No IO resource\n");
+
+ return -ENXIO;
+ }
+
+ lvts_data->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lvts_data->base)) {
+ dev_err(dev, "Failed to remap io\n");
+
+ return PTR_ERR(lvts_data->base);
+ }
+
+ /* Get interrupt number */
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "No irq resource\n");
+
+ return ret;
+ }
+
+ lvts_data->irq_num = ret;
+ /* Get reset control */
+ lvts_data->reset = devm_reset_control_get_by_index(dev, 0);
+ if (IS_ERR(lvts_data->reset)) {
+ dev_err(dev, "Failed to get reset control\n");
+
+ return PTR_ERR(lvts_data->reset);
+ }
+
+ ret = lvts_init_tc_regs(dev, lvts_data);
+ if (ret)
+ return ret;
+
+ ret = get_calibration_data(lvts_data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void lvts_device_close(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ unsigned int i;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ lvts_write_device(lvts_data, RESET_ALL_DEVICES, i);
+ writel(DISABLE_LVTS_CTRL_CLK, LVTSCLKEN_0 + base);
+ }
+}
+
+static void lvts_close(struct lvts_data *lvts_data)
+{
+ disable_all_sensing_points(lvts_data);
+ wait_all_tc_sensing_point_idle(lvts_data);
+ lvts_device_close(lvts_data);
+ clk_disable_unprepare(lvts_data->clk);
+}
+
+static void tc_irq_handler(struct lvts_data *lvts_data, int tc_id)
+{
+ void __iomem *base = GET_BASE_ADDR(lvts_data, tc_id);
+ const struct device *dev = lvts_data->dev;
+ unsigned int ret = readl(LVTSMONINTSTS_0 + base);
+
+ /* Write back to clear interrupt status */
+ writel(ret, LVTSMONINTSTS_0 + base);
+ dev_dbg(dev, "LVTS thermal controller %d, LVTSMONINTSTS=0x%08x\n", tc_id, ret);
+ if (ret & THERMAL_PROTECTION_STAGE_3)
+ dev_dbg(dev, "Thermal protection stage 3 interrupt triggered\n");
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+ void __iomem *base;
+ struct lvts_data *lvts_data = (struct lvts_data *)dev_id;
+ struct device *dev = lvts_data->dev;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ unsigned int i, irq_bitmap;
+
+ base = lvts_data->base;
+ irq_bitmap = readl(THERMINTST + base);
+ dev_dbg(dev, "THERMINTST = 0x%x\n", irq_bitmap);
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ if (tc[i].irq_bit == 0)
+ tc_irq_handler(lvts_data, i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int lvts_register_irq_handler(struct lvts_data *lvts_data)
+{
+ struct device *dev = lvts_data->dev;
+ int ret;
+
+ ret = devm_request_irq(dev, lvts_data->irq_num, irq_handler, IRQF_TRIGGER_NONE,
+ "mtk_lvts", lvts_data);
+ if (ret) {
+ dev_err(dev, "Failed to register LVTS IRQ, ret %d, irq_num %d\n",
+ ret, lvts_data->irq_num);
+ lvts_close(lvts_data);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lvts_register_thermal_zones(struct lvts_data *lvts_data)
+{
+ struct device *dev = lvts_data->dev;
+ struct thermal_zone_device *tzdev;
+ struct soc_temp_tz *lvts_tz;
+ int i, ret;
+
+ for (i = 0; i < lvts_data->num_sensor; i++) {
+ lvts_tz = devm_kzalloc(dev, sizeof(*lvts_tz), GFP_KERNEL);
+ if (!lvts_tz) {
+ lvts_close(lvts_data);
+
+ return -ENOMEM;
+ }
+
+ lvts_tz->id = i;
+ lvts_tz->lvts_data = lvts_data;
+ tzdev = devm_thermal_zone_of_sensor_register(dev, lvts_tz->id, lvts_tz, &soc_temp_lvts_ops);
+ if (IS_ERR(tzdev)) {
+ if (lvts_tz->id != 0)
+ return 0;
+
+ ret = PTR_ERR(tzdev);
+ dev_err(dev, "Failed to register lvts tz %d, ret = %d\n", lvts_tz->id, ret);
+ lvts_close(lvts_data);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void lvts_device_enable_and_init(struct lvts_data *lvts_data)
+{
+ unsigned int i;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ lvts_write_device(lvts_data, STOP_COUNTING_V4, i);
+ lvts_write_device(lvts_data, SET_RG_TSFM_LPDLY_V4, i);
+ lvts_write_device(lvts_data, SET_COUNTING_WINDOW_20US1_V4, i);
+ lvts_write_device(lvts_data, SET_COUNTING_WINDOW_20US2_V4, i);
+ lvts_write_device(lvts_data, TSV2F_CHOP_CKSEL_AND_TSV2F_EN_V4, i);
+ lvts_write_device(lvts_data, TSBG_DEM_CKSEL_X_TSBG_CHOP_EN_V4, i);
+ lvts_write_device(lvts_data, SET_TS_RSV_V4, i);
+ lvts_write_device(lvts_data, SET_TS_EN_V4, i);
+ lvts_write_device(lvts_data, TOGGLE_RG_TSV2F_VCO_RST1_V4, i);
+ lvts_write_device(lvts_data, TOGGLE_RG_TSV2F_VCO_RST2_V4, i);
+ }
+
+ lvts_data->counting_window_us = 20;
+}
+EXPORT_SYMBOL_GPL(lvts_device_enable_and_init);
+
+void lvts_device_enable_auto_rck_v4(struct lvts_data *lvts_data)
+{
+ unsigned int i;
+
+ for (i = 0; i < lvts_data->num_tc; i++)
+ lvts_write_device(lvts_data, SET_LVTS_AUTO_RCK_V4, i);
+}
+EXPORT_SYMBOL_GPL(lvts_device_enable_auto_rck_v4);
+
+int lvts_device_read_count_rc_n_v4(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ struct device *dev = lvts_data->dev;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ struct lvts_sensor_cal_data *cal_data = &lvts_data->cal_data;
+ unsigned int offset, s_index, data;
+ int ret, i, j;
+ char buffer[128];
+
+ cal_data->count_rc_now = devm_kcalloc(dev, lvts_data->num_sensor,
+ sizeof(*cal_data->count_rc_now), GFP_KERNEL);
+ if (!cal_data->count_rc_now)
+ return -ENOMEM;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ for (j = 0; j < tc[i].num_sensor; j++) {
+ s_index = tc[i].sensor_map[j];
+ lvts_write_device(lvts_data, SELECT_SENSOR_RCK_V4(j), i);
+ lvts_write_device(lvts_data, SET_DEVICE_SINGLE_MODE_V4, i);
+ lvts_write_device(lvts_data, KICK_OFF_RCK_COUNTING_V4, i);
+ ret = readl_poll_timeout(LVTS_CONFIG_0 + base, data,
+ !(data & DEVICE_SENSING_STATUS), 2, 200);
+ if (ret)
+ dev_err(dev, "LVTS_TC_%d DEVICE_SENSING_STATUS didn't ready\n", i);
+
+ data = lvts_read_device(lvts_data, 0x00, i);
+ cal_data->count_rc_now[s_index] = (data & COUNT_RC_NOW_MASK);
+ }
+
+ /* Recover Setting for Normal Access on
+ * temperature fetch
+ */
+ lvts_write_device(lvts_data, SET_SENSOR_NO_RCK_V4, i);
+ lvts_write_device(lvts_data, SET_DEVICE_LOW_POWER_SINGLE_MODE_V4, i);
+ }
+
+ offset = snprintf(buffer, sizeof(buffer), "[COUNT_RC_NOW] ");
+ for (i = 0; i < lvts_data->num_sensor; i++)
+ offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%d:%d ", i,
+ cal_data->count_rc_now[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lvts_device_read_count_rc_n_v4);
+
+void lvts_set_calibration_data_v4(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ const struct lvts_tc_settings *tc = lvts_data->tc;
+ struct lvts_sensor_cal_data *cal_data = &lvts_data->cal_data;
+ unsigned int i, j, s_index;
+ u32 lvts_calib_data;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ for (j = 0; j < tc[i].num_sensor; j++) {
+ s_index = tc[i].sensor_map[j];
+ if (HAS_FEATURE(lvts_data, FEATURE_DEVICE_AUTO_RCK))
+ lvts_calib_data = cal_data->count_r[s_index];
+
+ else
+ lvts_calib_data = (((u32)cal_data->count_rc_now[s_index]) *
+ cal_data->count_r[s_index]) >> 14;
+
+ writel(lvts_calib_data, LVTSEDATA00_0 + base + 0x4 * j);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(lvts_set_calibration_data_v4);
+
+void lvts_init_controller_v4(struct lvts_data *lvts_data)
+{
+ void __iomem *base;
+ struct device *dev = lvts_data->dev;
+ unsigned int i;
+
+ for (i = 0; i < lvts_data->num_tc; i++) {
+ base = GET_BASE_ADDR(lvts_data, i);
+ lvts_write_device(lvts_data, SET_DEVICE_LOW_POWER_SINGLE_MODE_V4, i);
+ writel(SET_SENSOR_INDEX, LVTSTSSEL_0 + base);
+ writel(SET_CALC_SCALE_RULES, LVTSCALSCALE_0 + base);
+ set_polling_speed(lvts_data, i);
+ set_hw_filter(lvts_data, i);
+ dev_dbg(dev, "lvts_tc_%d: read all %d sensors in %d us, one in %d us\n",
+ i, GET_TC_SENSOR_NUM(lvts_data, i), GROUP_LATENCY_US(i), SENSOR_LATENCY_US(i));
+ }
+}
+EXPORT_SYMBOL_GPL(lvts_init_controller_v4);
+
+int lvts_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lvts_data *lvts_data;
+ int ret;
+
+ lvts_data = (struct lvts_data *)of_device_get_match_data(dev);
+ if (!lvts_data) {
+ dev_err(dev, "Failed to get lvts platform data\n");
+
+ return -ENODATA;
+ }
+
+ lvts_data->dev = &pdev->dev;
+ ret = of_update_lvts_data(lvts_data, pdev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, lvts_data);
+ ret = lvts_init(lvts_data);
+ if (ret)
+ return ret;
+
+ ret = lvts_register_irq_handler(lvts_data);
+ if (ret)
+ return ret;
+
+ ret = lvts_register_thermal_zones(lvts_data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int lvts_remove(struct platform_device *pdev)
+{
+ struct lvts_data *lvts_data;
+
+ lvts_data = (struct lvts_data *)platform_get_drvdata(pdev);
+ lvts_close(lvts_data);
+
+ return 0;
+}
+
+int lvts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct lvts_data *lvts_data;
+
+ lvts_data = (struct lvts_data *)platform_get_drvdata(pdev);
+ lvts_close(lvts_data);
+
+ return 0;
+}
+
+int lvts_resume(struct platform_device *pdev)
+{
+ struct lvts_data *lvts_data;
+ int ret;
+
+ lvts_data = (struct lvts_data *)platform_get_drvdata(pdev);
+ ret = lvts_init(lvts_data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+MODULE_AUTHOR("Yu-Chia Chang <[email protected]>");
+MODULE_AUTHOR("Michael Kao <[email protected]>");
+MODULE_DESCRIPTION("MediaTek LVTS Thermal Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/mediatek/lvts_thermal.h b/drivers/thermal/mediatek/lvts_thermal.h
new file mode 100644
index 000000000000..9095f9cd232b
--- /dev/null
+++ b/drivers/thermal/mediatek/lvts_thermal.h
@@ -0,0 +1,376 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef __MTK_SOC_TEMP_LVTS_H__
+#define __MTK_SOC_TEMP_LVTS_H__
+
+#define PERIOD_UNIT 12
+#define GROUP_INTERVAL_DELAY 1
+#define FILTER_INTERVAL_DELAY 1
+#define SENSOR_INTERVAL_DELAY 1
+
+#define HW_REBOOT_TRIP_POINT 117000
+
+#define FEATURE_DEVICE_AUTO_RCK BIT(0)
+#define NUM_EFUSE_ADDR 22
+#define NUM_EFUSE_BLOCK_MT8192 1
+#define DEFAULT_GOLDEN_TEMP 50
+#define DEFAULT_CUONT_R 35000
+#define DEFAULT_CUONT_RC 2750
+#define COEFF_A -250460
+#define COEFF_B 250460
+
+#define CLOCK_26MHZ_CYCLE_NS 38
+#define BUS_ACCESS_US 2
+#define GOLDEN_TEMP_MAX 62
+
+#define TC_SENSING_POINT_0 BIT(0)
+#define TC_SENSING_POINT_7 BIT(7)
+#define TC_SENSING_POINT_10 BIT(10)
+#define ALL_TC_SENSING_POINT_STATUS (BIT(10) | BIT(7) | BIT(0))
+#define COUNT_RC_NOW_MASK GENMASK(23, 0)
+#define PERIOD_UNIT_MASK GENMASK(9, 0)
+#define GROUP_INTERVAL_DELAY_MASK GENMASK(29, 20)
+#define FILTER_INTERVAL_DELAY_MASK GENMASK(25, 16)
+#define SENSOR_INTERVAL_DELAY_MASK GENMASK(9, 0)
+
+/* LVTS device register */
+#define RG_TSFM_DATA_0 0x00
+#define RG_TSFM_DATA_1 0x01
+#define RG_TSFM_DATA_2 0x02
+#define RG_TSFM_CTRL_0 0x03
+#define RG_TSFM_CTRL_1 0x04
+#define RG_TSFM_CTRL_2 0x05
+#define RG_TSFM_CTRL_3 0x06
+#define RG_TSFM_CTRL_4 0x07
+#define RG_TSV2F_CTRL_0 0x08
+#define RG_TSV2F_CTRL_1 0x09
+#define RG_TSV2F_CTRL_2 0x0A
+#define RG_TSV2F_CTRL_3 0x0B
+#define RG_TSV2F_CTRL_4 0x0C
+#define RG_TSV2F_CTRL_5 0x0D
+#define RG_TSV2F_CTRL_6 0x0E
+#define RG_TEMP_DATA_0 0x10
+#define RG_TEMP_DATA_1 0x11
+#define RG_TEMP_DATA_2 0x12
+#define RG_TEMP_DATA_3 0x13
+#define RG_RC_DATA_0 0x14
+#define RG_RC_DATA_1 0x15
+#define RG_RC_DATA_2 0x16
+#define RG_RC_DATA_3 0x17
+#define RG_DIV_DATA_0 0x18
+#define RG_DIV_DATA_1 0x19
+#define RG_DIV_DATA_2 0x1A
+#define RG_DIV_DATA_3 0x1B
+#define RG_TST_DATA_0 0x70
+#define RG_TST_DATA_1 0x71
+#define RG_TST_DATA_2 0x72
+#define RG_TST_CTRL 0x73
+#define RG_DBG_FQMTR 0xF0
+#define RG_DBG_LPSEQ 0xF1
+#define RG_DBG_STATE 0xF2
+#define RG_DBG_CHKSUM 0xF3
+#define RG_DID_LVTS 0xFC
+#define RG_DID_REV 0xFD
+#define RG_TSFM_RST 0xFF
+
+/* LVTS controller register */
+#define LVTSMONCTL0_0 0x000
+#define ENABLE_SENSING_POINT(num) (LVTS_SINGLE_SENSE | GENMASK(((num) - 1), 0))
+#define DISABLE_SENSING_POINT (LVTS_SINGLE_SENSE | 0x0)
+#define LVTSMONCTL1_0 0x004
+#define LVTSMONCTL2_0 0x008
+#define LVTSMONINT_0 0x00C
+#define STAGE3_INT_EN BIT(31)
+#define LVTSMONINTSTS_0 0x010
+#define LVTSMONIDET0_0 0x014
+#define LVTSMONIDET1_0 0x018
+#define LVTSMONIDET2_0 0x01C
+#define LVTSMONIDET3_0 0x020
+#define LVTSH2NTHRE_0 0x024
+#define LVTSHTHRE_0 0x028
+#define LVTSCTHRE_0 0x02C
+#define LVTSOFFSETH_0 0x030
+#define LVTSOFFSETL_0 0x034
+#define LVTSMSRCTL0_0 0x038
+#define LVTSMSRCTL1_0 0x03C
+#define LVTSTSSEL_0 0x040
+#define SET_SENSOR_INDEX 0x13121110
+#define LVTSDEVICETO_0 0x044
+#define LVTSCALSCALE_0 0x048
+#define SET_CALC_SCALE_RULES 0x00000300
+#define LVTS_ID_0 0x04C
+#define LVTS_CONFIG_0 0x050
+
+#define SCK_ONLY BIT(31)
+#define BROADCAST_ID_UPDATE BIT(26)
+#define DEVICE_SENSING_STATUS BIT(25)
+#define DEVICE_ACCESS_STARTUS BIT(24)
+#define READ_32BIT_ACCESS BIT(17)
+#define WRITE_ACCESS BIT(16)
+#define LVTS_SINGLE_SENSE BIT(9)
+#define FEATURE_CK26M_ACTIVE BIT(1)
+#define DEVICE_REG_DATA GENMASK(7, 0)
+
+#define LVTSEDATA00_0 0x054
+#define LVTSEDATA01_0 0x058
+#define LVTSEDATA02_0 0x05C
+#define LVTSEDATA03_0 0x060
+#define LVTSMSR0_0 0x090
+#define MRS_RAW_MASK GENMASK(15, 0)
+#define MRS_RAW_VALID_BIT BIT(16)
+#define LVTSMSR1_0 0x094
+#define LVTSMSR2_0 0x098
+#define LVTSMSR3_0 0x09C
+#define LVTSIMMD0_0 0x0A0
+#define LVTSIMMD1_0 0x0A4
+#define LVTSIMMD2_0 0x0A8
+#define LVTSIMMD3_0 0x0AC
+#define LVTSRDATA0_0 0x0B0
+#define LVTSRDATA1_0 0x0B4
+#define LVTSRDATA2_0 0x0B8
+#define LVTSRDATA3_0 0x0BC
+#define LVTSPROTCTL_0 0x0C0
+#define PROTOFFSET GENMASK(15, 0)
+#define LVTSPROTTA_0 0x0C4
+#define LVTSPROTTB_0 0x0C8
+#define LVTSPROTTC_0 0x0CC
+#define LVTSCLKEN_0 0x0E4
+#define ENABLE_LVTS_CTRL_CLK (1)
+#define DISABLE_LVTS_CTRL_CLK (0)
+#define LVTSDBGSEL_0 0x0E8
+#define LVTSDBGSIG_0 0x0EC
+#define LVTSSPARE0_0 0x0F0
+#define LVTSSPARE1_0 0x0F4
+#define LVTSSPARE2_0 0x0F8
+#define LVTSSPARE3_0 0x0FC
+#define THERMINTST 0xF04
+
+/* LVTS register mask */
+#define THERMAL_COLD_INTERRUPT_0 BIT(0)
+#define THERMAL_HOT_INTERRUPT_0 BIT(1)
+#define THERMAL_LOW_OFFSET_INTERRUPT_0 BIT(2)
+#define THERMAL_HIGH_OFFSET_INTERRUPT_0 BIT(3)
+#define THERMAL_HOT2NORMAL_INTERRUPT_0 BIT(4)
+#define THERMAL_COLD_INTERRUPT_1 BIT(5)
+#define THERMAL_HOT_INTERRUPT_1 BIT(6)
+#define THERMAL_LOW_OFFSET_INTERRUPT_1 BIT(7)
+#define THERMAL_HIGH_OFFSET_INTERRUPT_1 BIT(8)
+#define THERMAL_HOT2NORMAL_INTERRUPT_1 BIT(9)
+#define THERMAL_COLD_INTERRUPT_2 BIT(10)
+#define THERMAL_HOT_INTERRUPT_2 BIT(11)
+#define THERMAL_LOW_OFFSET_INTERRUPT_2 BIT(12)
+#define THERMAL_HIGH_OFFSET_INTERRUPT_2 BIT(13)
+#define THERMAL_HOT2NORMAL_INTERRUPT_2 BIT(14)
+#define THERMAL_AHB_TIMEOUT_INTERRUPT BIT(15)
+#define THERMAL_DEVICE_TIMEOUT_INTERRUPT BIT(15)
+#define THERMAL_IMMEDIATE_INTERRUPT_0 BIT(16)
+#define THERMAL_IMMEDIATE_INTERRUPT_1 BIT(17)
+#define THERMAL_IMMEDIATE_INTERRUPT_2 BIT(18)
+#define THERMAL_FILTER_INTERRUPT_0 BIT(19)
+#define THERMAL_FILTER_INTERRUPT_1 BIT(20)
+#define THERMAL_FILTER_INTERRUPT_2 BIT(21)
+#define THERMAL_COLD_INTERRUPT_3 BIT(22)
+#define THERMAL_HOT_INTERRUPT_3 BIT(23)
+#define THERMAL_LOW_OFFSET_INTERRUPT_3 BIT(24)
+#define THERMAL_HIGH_OFFSET_INTERRUPT_3 BIT(25)
+#define THERMAL_HOT2NORMAL_INTERRUPT_3 BIT(26)
+#define THERMAL_IMMEDIATE_INTERRUPT_3 BIT(27)
+#define THERMAL_FILTER_INTERRUPT_3 BIT(28)
+#define THERMAL_PROTECTION_STAGE_1 BIT(29)
+#define THERMAL_PROTECTION_STAGE_2 BIT(30)
+#define THERMAL_PROTECTION_STAGE_3 BIT(31)
+
+#define CFG_REGISTER(reg, value) (reg << 8 | value)
+
+#define STOP_COUNTING_V4 CFG_REGISTER(RG_TSFM_CTRL_0, 0x00)
+#define SET_RG_TSFM_LPDLY_V4 CFG_REGISTER(RG_TSFM_CTRL_4, 0xA6)
+#define SET_COUNTING_WINDOW_20US1_V4 CFG_REGISTER(RG_TSFM_CTRL_2, 0x00)
+#define SET_COUNTING_WINDOW_20US2_V4 CFG_REGISTER(RG_TSFM_CTRL_1, 0x20)
+#define TSV2F_CHOP_CKSEL_AND_TSV2F_EN_V4 CFG_REGISTER(RG_TSV2F_CTRL_2, 0x84)
+#define TSBG_DEM_CKSEL_X_TSBG_CHOP_EN_V4 CFG_REGISTER(RG_TSV2F_CTRL_4, 0x7C)
+#define SET_TS_RSV_V4 CFG_REGISTER(RG_TSV2F_CTRL_1, 0x8D)
+#define SET_TS_EN_V4 CFG_REGISTER(RG_TSV2F_CTRL_0, 0xF4)
+#define TOGGLE_RG_TSV2F_VCO_RST1_V4 CFG_REGISTER(RG_TSV2F_CTRL_0, 0xFC)
+#define TOGGLE_RG_TSV2F_VCO_RST2_V4 CFG_REGISTER(RG_TSV2F_CTRL_0, 0xF4)
+
+#define SET_LVTS_AUTO_RCK_V4 CFG_REGISTER(RG_TSV2F_CTRL_6, 0x01)
+#define SELECT_SENSOR_RCK_V4(id) CFG_REGISTER(RG_TSV2F_CTRL_5, (id))
+#define SET_DEVICE_SINGLE_MODE_V4 CFG_REGISTER(RG_TSFM_CTRL_3, 0x78)
+#define KICK_OFF_RCK_COUNTING_V4 CFG_REGISTER(RG_TSFM_CTRL_0, 0x02)
+#define SET_SENSOR_NO_RCK_V4 CFG_REGISTER(RG_TSV2F_CTRL_5, 0x10)
+#define SET_DEVICE_LOW_POWER_SINGLE_MODE_V4 CFG_REGISTER(RG_TSFM_CTRL_3, 0xB8)
+
+#define HAS_FEATURE(lvts_data, feature) (lvts_data->feature_bitmap & (feature))
+#define GET_BASE_ADDR(lvts_data, tc_id) (lvts_data->base + lvts_data->tc[tc_id].addr_offset)
+#define GET_CAL_DATA_BITMASK(index, lvts_data, h, l) (((index) < lvts_data->num_efuse_addr) ? \
+ ((lvts_data->efuse[(index)] & GENMASK(h, l)) >> l) : 0)
+
+#define GET_TC_SENSOR_NUM(lvts_data, tc_id) (lvts_data->tc[tc_id].num_sensor)
+#define ONE_SAMPLE (lvts_data->counting_window_us + 2 * BUS_ACCESS_US)
+#define NUM_OF_SAMPLE(tc_id) ((lvts_data->tc[tc_id].hw_filter < LVTS_FILTER_2) ? \
+ 1 : ((lvts_data->tc[tc_id].hw_filter > LVTS_FILTER_16_OF_18) ? \
+ 1 : ((lvts_data->tc[tc_id].hw_filter == LVTS_FILTER_16_OF_18) ? \
+ 18 : ((lvts_data->tc[tc_id].hw_filter == LVTS_FILTER_8_OF_10) ? \
+ 10 : (lvts_data->tc[tc_id].hw_filter * 2)))))
+
+#define PERIOD_UNIT_US(tc_id) ((lvts_data->tc[tc_id].tc_speed->period_unit * 256 * \
+ CLOCK_26MHZ_CYCLE_NS) / 1000)
+#define FILTER_INT_US(tc_id) (lvts_data->tc[tc_id].tc_speed->filter_interval_delay * \
+ PERIOD_UNIT_US(tc_id))
+#define SENSOR_INT_US(tc_id) (lvts_data->tc[tc_id].tc_speed->sensor_interval_delay * \
+ PERIOD_UNIT_US(tc_id))
+#define GROUP_INT_US(tc_id) (lvts_data->tc[tc_id].tc_speed->group_interval_delay * \
+ PERIOD_UNIT_US(tc_id))
+#define SENSOR_LATENCY_US(tc_id) ((NUM_OF_SAMPLE(tc_id) - 1) * FILTER_INT_US(tc_id) + \
+ NUM_OF_SAMPLE(tc_id) * ONE_SAMPLE)
+#define GROUP_LATENCY_US(tc_id) (GET_TC_SENSOR_NUM(lvts_data, tc_id) * \
+ SENSOR_LATENCY_US(tc_id) + (GET_TC_SENSOR_NUM(lvts_data, tc_id) - 1) * \
+ SENSOR_INT_US(tc_id) + GROUP_INT_US(tc_id))
+
+#define CK26M_ACTIVE(lvts_data) (((lvts_data->feature_bitmap & FEATURE_CK26M_ACTIVE) ? 1 : 0) << 30)
+#define DEVICE_ACCESS (SCK_ONLY | DEVICE_ACCESS_STARTUS | READ_32BIT_ACCESS)
+#define DEVICE_READ (CK26M_ACTIVE(lvts_data) | DEVICE_ACCESS)
+#define DEVICE_WRITE (CK26M_ACTIVE(lvts_data) | DEVICE_ACCESS | WRITE_ACCESS)
+#define RESET_ALL_DEVICES (DEVICE_WRITE | RG_TSFM_RST << 8 | 0xFF)
+#define READ_DEVICE_REG(reg_id) (DEVICE_READ | (reg_id) << 8 | 0x00)
+#define READ_BACK_DEVICE_ID (CK26M_ACTIVE(lvts_data) | DEVICE_ACCESS | BROADCAST_ID_UPDATE | \
+ RG_DID_LVTS << 8)
+
+/*
+ * LVTS HW filter settings
+ * 000: Get one sample
+ * 001: Get 2 samples and average them
+ * 010: Get 4 samples, drop max and min, then average the rest of 2 samples
+ * 011: Get 6 samples, drop max and min, then average the rest of 4 samples
+ * 100: Get 10 samples, drop max and min, then average the rest of 8 samples
+ * 101: Get 18 samples, drop max and min, then average the rest of 16 samples
+ */
+enum lvts_hw_filter {
+ LVTS_FILTER_1,
+ LVTS_FILTER_2,
+ LVTS_FILTER_2_OF_4,
+ LVTS_FILTER_4_OF_6,
+ LVTS_FILTER_8_OF_10,
+ LVTS_FILTER_16_OF_18
+};
+
+enum lvts_sensing_point {
+ SENSING_POINT0,
+ SENSING_POINT1,
+ SENSING_POINT2,
+ SENSING_POINT3,
+ ALL_SENSING_POINTS
+};
+
+struct lvts_data;
+
+/**
+ * struct lvts_speed_settings - A structure to hold the data related to polling rate
+ * @period_unit: Period unit is a base for all interval delays
+ * @group_interval_delay: Delay between different rounds
+ * @filter_interval_delay: Delay between two samples of the same sensor
+ * @sensor_interval_delay: Delay between two samples of differnet sensors
+ *
+ * Calculation is achieved with the following equations:
+ * For the period unit: (period_us * 1000) / (256 * clock_26mhz_cycle_ns)
+ * For the interval delays: delay / period_us
+ */
+struct lvts_speed_settings {
+ unsigned int period_unit;
+ unsigned int group_interval_delay;
+ unsigned int filter_interval_delay;
+ unsigned int sensor_interval_delay;
+};
+
+struct lvts_tc_settings {
+ unsigned int dev_id;
+ unsigned int addr_offset;
+ unsigned int num_sensor;
+ unsigned int sensor_map[ALL_SENSING_POINTS]; /* In sensor ID */
+ struct lvts_speed_settings *tc_speed;
+ /*
+ * HW filter setting
+ * 000: Get one sample
+ * 001: Get 2 samples and average them
+ * 010: Get 4 samples, drop max and min, then average the rest of 2 samples
+ * 011: Get 6 samples, drop max and min, then average the rest of 4 samples
+ * 100: Get 10 samples, drop max and min, then average the rest of 8 samples
+ * 101: Get 18 samples, drop max and min, then average the rest of 16 samples
+ */
+ unsigned int hw_filter;
+ /*
+ * Dominator_sensing point is used to select a sensing point
+ * and reference its temperature to trigger Thermal HW Reboot
+ * When it is ALL_SENSING_POINTS, it will select all sensing points
+ */
+ int dominator_sensing_point;
+ int hw_reboot_trip_point; /* -274000: Disable HW reboot */
+ unsigned int irq_bit;
+};
+
+struct lvts_formula_coeff {
+ int a;
+ int b;
+ unsigned int golden_temp;
+};
+
+struct lvts_sensor_cal_data {
+ int use_fake_efuse; /* 1: Use fake efuse, 0: Use real efuse */
+ unsigned int golden_temp;
+ unsigned int *count_r;
+ unsigned int *count_rc;
+ unsigned int *count_rc_now;
+ unsigned int default_golden_temp;
+ unsigned int default_count_r;
+ unsigned int default_count_rc;
+};
+
+struct platform_ops {
+ void (*efuse_to_cal_data)(struct lvts_data *lvts_data);
+ void (*device_enable_and_init)(struct lvts_data *lvts_data);
+ void (*device_enable_auto_rck)(struct lvts_data *lvts_data);
+ int (*device_read_count_rc_n)(struct lvts_data *lvts_data);
+ void (*set_cal_data)(struct lvts_data *lvts_data);
+ void (*init_controller)(struct lvts_data *lvts_data);
+};
+
+struct lvts_data {
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *base; /* LVTS base addresses */
+ unsigned int irq_num; /* LVTS interrupt numbers */
+ struct reset_control *reset;
+ int num_tc; /* Number of LVTS thermal controllers */
+ const struct lvts_tc_settings *tc;
+ int counting_window_us; /* LVTS device counting window */
+ int num_sensor; /* Number of sensors in this platform */
+ void __iomem **reg;
+ struct platform_ops ops;
+ int feature_bitmap; /* Show what features are enabled */
+ unsigned int num_efuse_addr;
+ unsigned int *efuse;
+ unsigned int num_efuse_block; /* Number of contiguous efuse indexes */
+ struct lvts_sensor_cal_data cal_data;
+ struct lvts_formula_coeff coeff;
+};
+
+struct soc_temp_tz {
+ unsigned int id;
+ struct lvts_data *lvts_data;
+};
+
+extern void lvts_device_enable_and_init(struct lvts_data *lvts_data);
+extern void lvts_device_enable_auto_rck_v4(struct lvts_data *lvts_data);
+extern int lvts_device_read_count_rc_n_v4(struct lvts_data *lvts_data);
+extern void lvts_set_calibration_data_v4(struct lvts_data *lvts_data);
+extern void lvts_init_controller_v4(struct lvts_data *lvts_data);
+
+extern int lvts_probe(struct platform_device *pdev);
+extern int lvts_remove(struct platform_device *pdev);
+extern int lvts_suspend(struct platform_device *pdev, pm_message_t state);
+extern int lvts_resume(struct platform_device *pdev);
+
+#endif /* __MTK_SOC_TEMP_LVTS_H__ */
diff --git a/drivers/thermal/mediatek/lvts_v4.c b/drivers/thermal/mediatek/lvts_v4.c
new file mode 100644
index 000000000000..6477d386c9e1
--- /dev/null
+++ b/drivers/thermal/mediatek/lvts_v4.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include "lvts_thermal.h"
+
+enum mt8192_lvts_mcu_sensor_enum {
+ MT8192_TS1_0,
+ MT8192_TS1_1,
+ MT8192_TS2_0,
+ MT8192_TS2_1,
+ MT8192_TS3_0,
+ MT8192_TS3_1,
+ MT8192_TS3_2,
+ MT8192_TS3_3,
+ MT8192_NUM_TS_MCU
+};
+
+enum mt8192_lvts_ap_sensor_enum {
+ MT8192_TS4_0,
+ MT8192_TS4_1,
+ MT8192_TS5_0,
+ MT8192_TS5_1,
+ MT8192_TS6_0,
+ MT8192_TS6_1,
+ MT8192_TS7_0,
+ MT8192_TS7_1,
+ MT8192_TS7_2,
+ MT8192_NUM_TS_AP
+};
+
+static void mt8192_mcu_efuse_to_cal_data(struct lvts_data *lvts_data)
+{
+ const unsigned int mt8192_ts[] = { MT8192_TS2_0, MT8192_TS3_0 };
+ struct lvts_sensor_cal_data *cal_data = &lvts_data->cal_data;
+ unsigned int i, j;
+
+ cal_data->golden_temp = GET_CAL_DATA_BITMASK(0, lvts_data, 31, 24);
+
+ for (i = 0; i < MT8192_NUM_TS_MCU; i++)
+ cal_data->count_r[i] = GET_CAL_DATA_BITMASK(i + 1, lvts_data, 23, 0);
+
+ cal_data->count_rc[MT8192_TS1_0] = GET_CAL_DATA_BITMASK(21, lvts_data, 23, 0);
+
+ for (i = 0; i < (ARRAY_SIZE(mt8192_ts)); i++) {
+ for (j = 1; j <= 18; j++) {
+ cal_data->count_rc[mt8192_ts[i]] = (GET_CAL_DATA_BITMASK(j, lvts_data, 31, 24) << 16) +
+ (GET_CAL_DATA_BITMASK(j, lvts_data, 31, 24) << 8) +
+ GET_CAL_DATA_BITMASK(j, lvts_data, 31, 24);
+ }
+ }
+}
+
+static void mt8192_ap_efuse_to_cal_data(struct lvts_data *lvts_data)
+{
+ const unsigned int mt8192_ts[] = { MT8192_TS4_0, MT8192_TS5_0, MT8192_TS6_0, MT8192_TS7_0 };
+ struct lvts_sensor_cal_data *cal_data = &lvts_data->cal_data;
+ unsigned int i, j;
+
+ cal_data->golden_temp = GET_CAL_DATA_BITMASK(0, lvts_data, 31, 24);
+
+ for (i = 0; i < MT8192_NUM_TS_AP; i++)
+ cal_data->count_r[i] = GET_CAL_DATA_BITMASK(i + 1, lvts_data, 23, 0);
+
+ for (i = 0; i < (ARRAY_SIZE(mt8192_ts)); i++) {
+ for (j = 1; j <= 18; j++) {
+ cal_data->count_rc[mt8192_ts[i]] = (GET_CAL_DATA_BITMASK(j, lvts_data, 31, 24) << 16) +
+ (GET_CAL_DATA_BITMASK(j, lvts_data, 31, 24) << 8) +
+ GET_CAL_DATA_BITMASK(j, lvts_data, 31, 24);
+ }
+ }
+}
+
+static struct lvts_speed_settings tc_speed_mt8192 = {
+ .period_unit = PERIOD_UNIT,
+ .group_interval_delay = GROUP_INTERVAL_DELAY,
+ .filter_interval_delay = FILTER_INTERVAL_DELAY,
+ .sensor_interval_delay = SENSOR_INTERVAL_DELAY,
+};
+
+static const struct lvts_tc_settings mt8192_tc_mcu_settings[] = {
+ [0] = {
+ .dev_id = 0x81,
+ .addr_offset = 0x0,
+ .num_sensor = 2,
+ .sensor_map = { MT8192_TS1_0, MT8192_TS1_1 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT1,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(3),
+ },
+ [1] = {
+ .dev_id = 0x82,
+ .addr_offset = 0x100,
+ .num_sensor = 2,
+ .sensor_map = { MT8192_TS2_0, MT8192_TS2_1 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT0,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(4),
+ },
+ [2] = {
+ .dev_id = 0x83,
+ .addr_offset = 0x200,
+ .num_sensor = 4,
+ .sensor_map = { MT8192_TS3_0, MT8192_TS3_1, MT8192_TS3_2, MT8192_TS3_3 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT0,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(5),
+ }
+};
+
+static const struct lvts_tc_settings mt8192_tc_ap_settings[] = {
+ [0] = {
+ .dev_id = 0x84,
+ .addr_offset = 0x0,
+ .num_sensor = 2,
+ .sensor_map = { MT8192_TS4_0, MT8192_TS4_1 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT0,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(3),
+ },
+ [1] = {
+ .dev_id = 0x85,
+ .addr_offset = 0x100,
+ .num_sensor = 2,
+ .sensor_map = { MT8192_TS5_0, MT8192_TS5_1 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT1,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(4),
+ },
+ [2] = {
+ .dev_id = 0x86,
+ .addr_offset = 0x200,
+ .num_sensor = 2,
+ .sensor_map = { MT8192_TS6_0, MT8192_TS6_1 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT1,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(5),
+ },
+ [3] = {
+ .dev_id = 0x87,
+ .addr_offset = 0x300,
+ .num_sensor = 3,
+ .sensor_map = { MT8192_TS7_0, MT8192_TS7_1, MT8192_TS7_2 },
+ .tc_speed = &tc_speed_mt8192,
+ .hw_filter = LVTS_FILTER_2_OF_4,
+ .dominator_sensing_point = SENSING_POINT2,
+ .hw_reboot_trip_point = HW_REBOOT_TRIP_POINT,
+ .irq_bit = BIT(6),
+ }
+};
+
+static const struct lvts_data mt8192_lvts_mcu_data = {
+ .num_tc = (ARRAY_SIZE(mt8192_tc_mcu_settings)),
+ .tc = mt8192_tc_mcu_settings,
+ .num_sensor = MT8192_NUM_TS_MCU,
+ .ops = {
+ .efuse_to_cal_data = mt8192_mcu_efuse_to_cal_data,
+ .device_enable_and_init = lvts_device_enable_and_init,
+ .device_enable_auto_rck = lvts_device_enable_auto_rck_v4,
+ .device_read_count_rc_n = lvts_device_read_count_rc_n_v4,
+ .set_cal_data = lvts_set_calibration_data_v4,
+ .init_controller = lvts_init_controller_v4,
+ },
+ .feature_bitmap = FEATURE_DEVICE_AUTO_RCK,
+ .num_efuse_addr = NUM_EFUSE_ADDR,
+ .num_efuse_block = NUM_EFUSE_BLOCK_MT8192,
+ .cal_data = {
+ .default_golden_temp = DEFAULT_GOLDEN_TEMP,
+ .default_count_r = DEFAULT_CUONT_R,
+ .default_count_rc = DEFAULT_CUONT_RC,
+ },
+ .coeff = {
+ .a = COEFF_A,
+ .b = COEFF_B,
+ },
+};
+
+static const struct lvts_data mt8192_lvts_ap_data = {
+ .num_tc = (ARRAY_SIZE(mt8192_tc_ap_settings)),
+ .tc = mt8192_tc_ap_settings,
+ .num_sensor = MT8192_NUM_TS_AP,
+ .ops = {
+ .efuse_to_cal_data = mt8192_ap_efuse_to_cal_data,
+ .device_enable_and_init = lvts_device_enable_and_init,
+ .device_enable_auto_rck = lvts_device_enable_auto_rck_v4,
+ .device_read_count_rc_n = lvts_device_read_count_rc_n_v4,
+ .set_cal_data = lvts_set_calibration_data_v4,
+ .init_controller = lvts_init_controller_v4,
+ },
+ .feature_bitmap = FEATURE_DEVICE_AUTO_RCK,
+ .num_efuse_addr = NUM_EFUSE_ADDR,
+ .num_efuse_block = NUM_EFUSE_BLOCK_MT8192,
+ .cal_data = {
+ .default_golden_temp = DEFAULT_GOLDEN_TEMP,
+ .default_count_r = DEFAULT_CUONT_R,
+ .default_count_rc = DEFAULT_CUONT_RC,
+ },
+ .coeff = {
+ .a = COEFF_A,
+ .b = COEFF_B,
+ },
+};
+
+static const struct of_device_id lvts_of_match[] = {
+ { .compatible = "mediatek,mt8192-lvts-mcu", .data = &mt8192_lvts_mcu_data, },
+ { .compatible = "mediatek,mt8192-lvts-ap", .data = &mt8192_lvts_ap_data, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lvts_of_match);
+
+static struct platform_driver soc_temp_lvts = {
+ .probe = lvts_probe,
+ .remove = lvts_remove,
+ .suspend = lvts_suspend,
+ .resume = lvts_resume,
+ .driver = {
+ .name = "mtk-lvts-v4",
+ .of_match_table = lvts_of_match,
+ },
+};
+module_platform_driver(soc_temp_lvts);
+
+MODULE_AUTHOR("Yu-Chia Chang <[email protected]>");
+MODULE_AUTHOR("Michael Kao <[email protected]>");
+MODULE_DESCRIPTION("MediaTek LVTS V4 Thermal Driver");
+MODULE_LICENSE("GPL");
--
2.34.1


2022-08-04 14:02:07

by Balsam CHIHI

[permalink] [raw]
Subject: [PATCH v8.1, 2/7] dt-bindings: thermal: Add binding document for LVTS thermal controllers

From: Alexandre Bailon <[email protected]>

This patch adds dt-binding documents for mt8192 and mt8195 thermal controllers.

Signed-off-by: Alexandre Bailon <[email protected]>
Signed-off-by: Balsam CHIHI <[email protected]>
---
.../thermal/mediatek,lvts-thermal.yaml | 77 +++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml

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..53c44a73f3a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
@@ -0,0 +1,77 @@
+# 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 LVTS thermal controller
+
+maintainers:
+ - Yu-Chia Chang <[email protected]>
+ - Ben Tseng <[email protected]>
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8192-lvts-ap
+ - mediatek,mt8192-lvts-mcu
+ - mediatek,mt8195-lvts-ap
+ - mediatek,mt8195-lvts-mcu
+
+ "#thermal-sensor-cells":
+ const: 1
+
+ reg:
+ maxItems: 2
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: SW reset HW AP/MCU domain to clean temporary data on HW initialization/resume.
+
+ nvmem-cells:
+ items:
+ - description: LVTS calibration data 1 for thermal sensors
+ - description: LVTS calibration data 2 for thermal sensors
+
+ nvmem-cell-names:
+ items:
+ - const: lvts_calib_data1
+ - const: lvts_calib_data2
+
+required:
+ - compatible
+ - '#thermal-sensor-cells'
+ - reg
+ - interrupts
+ - clocks
+ - resets
+ - nvmem-cells
+ - nvmem-cell-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/thermal/thermal.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8195-clk.h>
+ #include <dt-bindings/reset/mt8195-resets.h>
+
+ lvtsmcu: thermal-sensor@11278000 {
+ compatible = "mediatek,mt8195-lvts-mcu";
+ #thermal-sensor-cells = <1>;
+ reg = <0 0x11278000 0 0x400>;
+ 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_data1", "lvts_calib_data2";
+ };
+
+...
--
2.34.1


2022-08-04 14:03:25

by Balsam CHIHI

[permalink] [raw]
Subject: [PATCH v8.1, 5/7] arm64: dts: mt8195: Add efuse node to mt8195

From: Alexandre Bailon <[email protected]>

This adds the efuse node. This will be required by the thermal driver
to get the calibration data.

Signed-off-by: Alexandre Bailon <[email protected]>
Signed-off-by: Balsam CHIHI <[email protected]>
---
arch/arm64/boot/dts/mediatek/mt8195.dtsi | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
index 73a0e2103b83..cbd0401968a2 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
- * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2022 MediaTek Inc.
* Author: Seiya Wang <[email protected]>
*/

@@ -733,6 +733,18 @@ u2_intr_p3: usb2-intr-p3@189,2 {
reg = <0x189 0x2>;
bits = <7 5>;
};
+
+ lvts_efuse_data1: lvts1-calib@1bc {
+ reg = <0x1bc 0x14>;
+ };
+
+ lvts_efuse_data2: lvts2-calib@1d0 {
+ reg = <0x1d0 0x38>;
+ };
+
+ svs_calibration: calib@580 {
+ reg = <0x580 0x64>;
+ };
};

u3phy2: t-phy@11c40000 {
--
2.34.1


2022-08-04 15:39:27

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v8.1, 5/7] arm64: dts: mt8195: Add efuse node to mt8195

On 04/08/2022 15:09, [email protected] wrote:
> From: Alexandre Bailon <[email protected]>
>
> This adds the efuse node. This will be required by the thermal driver
> to get the calibration data.
>
> Signed-off-by: Alexandre Bailon <[email protected]>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> arch/arm64/boot/dts/mediatek/mt8195.dtsi | 14 +++++++++++++-
> 1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> index 73a0e2103b83..cbd0401968a2 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: (GPL-2.0 OR MIT)
> /*
> - * Copyright (c) 2021 MediaTek Inc.
> + * Copyright (c) 2022 MediaTek Inc.

Why changing first copyright release date? This was not explained in
commit msg.



Best regards,
Krzysztof

2022-08-04 15:55:14

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v8.1, 2/7] dt-bindings: thermal: Add binding document for LVTS thermal controllers

On 04/08/2022 15:09, [email protected] wrote:
> From: Alexandre Bailon <[email protected]>
>
> This patch adds dt-binding documents for mt8192 and mt8195 thermal controllers.

Do not use "This commit/patch".
https://elixir.bootlin.com/linux/v5.17.1/source/Documentation/process/submitting-patches.rst#L95

>
> Signed-off-by: Alexandre Bailon <[email protected]>
> Signed-off-by: Balsam CHIHI <[email protected]>

Please rebase your patches on recent Linux kernel (or next).

> ---
> .../thermal/mediatek,lvts-thermal.yaml | 77 +++++++++++++++++++
> 1 file changed, 77 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
>
> 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..53c44a73f3a4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> @@ -0,0 +1,77 @@
> +# 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 LVTS thermal controller
> +
> +maintainers:
> + - Yu-Chia Chang <[email protected]>
> + - Ben Tseng <[email protected]>
> +

Any reason why not referencing thermal-sensor.yaml?

> +properties:
> + compatible:
> + enum:
> + - mediatek,mt8192-lvts-ap
> + - mediatek,mt8192-lvts-mcu
> + - mediatek,mt8195-lvts-ap
> + - mediatek,mt8195-lvts-mcu
> +
> + "#thermal-sensor-cells":
> + const: 1
> +
> + reg:
> + maxItems: 2

You need to describe the items (like nvmem-cells).

> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 1
> +
> + resets:
> + items:
> + - description: SW reset HW AP/MCU domain to clean temporary data on HW initialization/resume.
> +
> + nvmem-cells:
> + items:
> + - description: LVTS calibration data 1 for thermal sensors
> + - description: LVTS calibration data 2 for thermal sensors
> +
> + nvmem-cell-names:
> + items:
> + - const: lvts_calib_data1
> + - const: lvts_calib_data2
> +
> +required:
> + - compatible
> + - '#thermal-sensor-cells'
> + - reg
> + - interrupts
> + - clocks
> + - resets
> + - nvmem-cells
> + - nvmem-cell-names
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/thermal/thermal.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/clock/mt8195-clk.h>
> + #include <dt-bindings/reset/mt8195-resets.h>
> +
> + lvtsmcu: thermal-sensor@11278000 {

Mixed-up indentation.

Best regards,
Krzysztof

2022-08-04 18:48:25

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH v8.1, 4/7] thermal: mediatek: Add LVTS driver for mt8192 thermal zones

Hi--

On 8/4/22 06:09, [email protected] wrote:
> diff --git a/drivers/thermal/mediatek/Kconfig b/drivers/thermal/mediatek/Kconfig
> index 592c849b9fed..afef43a0e7ca 100644
> --- a/drivers/thermal/mediatek/Kconfig
> +++ b/drivers/thermal/mediatek/Kconfig
> @@ -20,4 +20,28 @@ config MTK_SOC_THERMAL
> configures thermal controllers to collect temperature
> via AUXADC interface.
>
> +config MTK_LVTS_THERMAL
> + tristate "LVTS (Low Voltage Thermal Sensor) driver for MediaTek SoCs"
> + depends on HAS_IOMEM
> + depends on NVMEM
> + depends on RESET_CONTROLLER
> + help
> + Enable this option if you want to get SoC temperature
> + information for MediaTek platforms. This driver configures
> + LVTS (Low Voltage Thermal Sensor) thermal controllers to
> + collect temperatures via ASIF (Analog Serial Interface).
> +
> +if MTK_LVTS_THERMAL
> +
> +config MTK_LVTS_V4
> + tristate "LVTS V4 Thermal Driver for MediaTek SoCs"
> + depends on HAS_IOMEM
> + depends on NVMEM
> + depends on RESET_CONTROLLER

You shouldn't need to repeat all of these "depends on" lines since this
Kconfig symbol depends on MTK_LVTS_THERMAL, which already depends on these
3 items.

Have you seen any issue that this is supposed to fix?

> + help
> + Enable this option if you want to get SoC temperature
> + information for LVTS V4.
> +
> +endif

thanks.
--
~Randy

2022-08-04 20:43:01

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v8.1, 2/7] dt-bindings: thermal: Add binding document for LVTS thermal controllers

On Thu, 04 Aug 2022 15:09:07 +0200, [email protected] wrote:
> From: Alexandre Bailon <[email protected]>
>
> This patch adds dt-binding documents for mt8192 and mt8195 thermal controllers.
>
> Signed-off-by: Alexandre Bailon <[email protected]>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> .../thermal/mediatek,lvts-thermal.yaml | 77 +++++++++++++++++++
> 1 file changed, 77 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
>

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.example.dts:32.36-37 syntax error
FATAL ERROR: Unable to parse input tree
make[1]: *** [scripts/Makefile.lib:383: Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.example.dtb] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1404: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


2022-08-04 23:31:38

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v8.1, 2/7] dt-bindings: thermal: Add binding document for LVTS thermal controllers

On Thu, Aug 04, 2022 at 03:09:07PM +0200, [email protected] wrote:
> From: Alexandre Bailon <[email protected]>
>
> This patch adds dt-binding documents for mt8192 and mt8195 thermal controllers.
>
> Signed-off-by: Alexandre Bailon <[email protected]>
> Signed-off-by: Balsam CHIHI <[email protected]>
> ---
> .../thermal/mediatek,lvts-thermal.yaml | 77 +++++++++++++++++++
> 1 file changed, 77 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
>
> 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..53c44a73f3a4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
[..]
> + nvmem-cells:

minItems: 1

since mt8192 only has one of them.

> + items:
> + - description: LVTS calibration data 1 for thermal sensors
> + - description: LVTS calibration data 2 for thermal sensors
> +
> + nvmem-cell-names:

Ditto.

Thanks,
N?colas

> + items:
> + - const: lvts_calib_data1
> + - const: lvts_calib_data2
[..]

2022-08-04 23:56:34

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v8.1, 0/7] Add LVTS architecture thermal

On Thu, Aug 04, 2022 at 03:09:05PM +0200, [email protected] wrote:
> From: Balsam CHIHI <[email protected]>
>
> This series moves thermal files related to MediaTek to the mediatek folder.
> And introduce the new architecture LVTS (low voltage thermal sensor) driver to report
> the highest temperature in the SoC and record the highest temperature sensor,
> each sensor as a hot zone.
> The LVTS body is divided into two parts, the LVTS controller and the LVTS device.
> The LVTS controller can connect up to 4 LVTS devices, and each LVTS device
> can connect up to 7 TSMCUs.
>
> The architecture will be the first to be used on mt8192 and mt8195.
>
> Changelog:
> Changes in v8.1 :

Please use whole numbers, no need to make this more confusing, v9 would've been
just fine :-).

Thanks,
N?colas

2022-08-05 00:06:31

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v8.1, 4/7] thermal: mediatek: Add LVTS driver for mt8192 thermal zones

On Thu, Aug 04, 2022 at 03:09:09PM +0200, [email protected] wrote:
> From: Michael Kao <[email protected]>
>
> Add a LVTS V4 (Low Voltage Thermal Sensor) driver to report junction
> temperatures in MediaTek SoC mt8192 and register the maximum temperature
> of sensors and each sensor as a thermal zone.
>
> Signed-off-by: Yu-Chia Chang <[email protected]>
> Signed-off-by: Michael Kao <[email protected]>
> Signed-off-by: Ben Tseng <[email protected]>
> Signed-off-by: Alexandre Bailon <[email protected]>
> Signed-off-by: Balsam CHIHI <[email protected]>

You should have a Co-developed-by tag for each person that wrote the code [1].

[1] https://www.kernel.org/doc/html/latest/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by

> ---
[..]
> --- /dev/null
> +++ b/drivers/thermal/mediatek/lvts_thermal.c
[..]
> +static int prepare_calibration_data(struct lvts_data *lvts_data)
> +{
[..]
> + lvts_data->coeff.golden_temp = cal_data->golden_temp;
> + dev_dbg(dev, "golden_temp = %d\n", cal_data->golden_temp);
> + offset = snprintf(buffer, sizeof(buffer), "[lvts_cal] num:g_count:g_count_rc ");
> + for (i = 0; i < lvts_data->num_sensor; i++)
> + offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%d:%d:%d ",
> + i, cal_data->count_r[i], cal_data->count_rc[i]);

Like Angelo already mentioned [2], you're not using this string for anything, so
just remove the code.

[2] https://lore.kernel.org/linux-mediatek/[email protected]/

> +
> + return 0;
> +}
[..]
> +int lvts_device_read_count_rc_n_v4(struct lvts_data *lvts_data)
> +{
[..]
> + offset = snprintf(buffer, sizeof(buffer), "[COUNT_RC_NOW] ");
> + for (i = 0; i < lvts_data->num_sensor; i++)
> + offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%d:%d ", i,
> + cal_data->count_rc_now[i]);

Again, just formatting a string and throwing it away, please just drop the code.

> +
> + return 0;
> +}
[..]
> +int lvts_resume(struct platform_device *pdev)
> +{
> + struct lvts_data *lvts_data;
> + int ret;
> +
> + lvts_data = (struct lvts_data *)platform_get_drvdata(pdev);
> + ret = lvts_init(lvts_data);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}

You have a bug in your resume path somewhere. I get this error during resume on
mt8192-asurada-spherion:

<1>[ 237.487428] Unable to handle kernel write to read-only memory at virtual address ffffc15096e491c0
<1>[ 237.496663] Mem abort info:
<1>[ 237.499794] ESR = 0x000000009600004e
<1>[ 237.504334] EC = 0x25: DABT (current EL), IL = 32 bits
<1>[ 237.509982] SET = 0, FnV = 0
<1>[ 237.513444] EA = 0, S1PTW = 0
<1>[ 237.516911] FSC = 0x0e: level 2 permission fault
<1>[ 237.522041] Data abort info:
<1>[ 237.525263] ISV = 0, ISS = 0x0000004e
<1>[ 237.529458] CM = 0, WnR = 1
<1>[ 237.532739] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000041782000
<1>[ 237.539754] [ffffc15096e491c0] pgd=100000023ffff003, p4d=100000023ffff003, pud=100000023fffe003, pmd=0060000041200f81
<0>[ 237.550821] Internal error: Oops: 9600004e [#1] PREEMPT SMP
<4>[ 237.556669] Modules linked in: af_alg mtk_vcodec_dec_hw snd_soc_hdmi_codec panel_edp mtk_vcodec_dec v4l2_vp9 v4l2_h264 mtk_vcodec_enc btusb mtk_vcodec_common btrtl videobuf2_dma_contig uvcvideo btintel videobuf2_vmalloc mtk_vpu btmtk videobuf2_memops v4l2_mem2mem mt7921e btbcm cdc_ether usbnet videobuf2_v4l2 mt7921_common bluetooth crct10dif_ce videobuf2_common r8152 mt76_connac_lib videodev ecdh_generic ecc mc mt76 mac80211 sbs_battery cros_usbpd_charger cros_usbpd_logger cros_ec_chardev libarc4 pwm_cros_ec anx7625 snd_soc_rt5682_i2c elan_i2c snd_soc_rt5682 cros_ec_typec snd_soc_rl6231 drm_dp_aux_bus typec drm_display_helper panfrost mediatek_drm drm_cma_helper rtc_mt6397 drm_shmem_helper phy_mtk_mipi_dsi_drv gpu_sched mt8192_mt6359_rt1015_rt5682 drm_kms_helper pwm_mtk_disp pwm_bl snd_soc_rt1015p snd_soc_mt8192_afe snd_soc_dmic snd_soc_mtk_common cfg80211 rfkill drm fuse backlight ip_tables x_tables ipv6
<4>[ 237.637538] CPU: 1 PID: 533 Comm: bash Tainted: G W 5.19.0-rc8-next-20220725+ #254
<4>[ 237.646781] Hardware name: Google Spherion (rev0 - 3) (DT)
<4>[ 237.652539] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
<4>[ 237.659778] pc : lvts_device_enable_and_init+0xdc/0xf0
<4>[ 237.665198] lr : lvts_device_enable_and_init+0xc8/0xf0
<4>[ 237.670609] sp : ffff8000098eb9b0
<4>[ 237.674188] x29: ffff8000098eb9b0 x28: 0000000000000000 x27: 0000000000000003
<4>[ 237.681609] x26: ffff41b141151010 x25: ffffc15097260c50 x24: 0000000000000000
<4>[ 237.689028] x23: 0000000000000038 x22: 0000000000000001 x21: 000000008502fc00
<4>[ 237.696448] x20: ffffc15096e49188 x19: 0000000000000003 x18: 0000000000000000
<4>[ 237.703867] x17: 0000000000000001 x16: ffffc15095cc9838 x15: 00000000000001ca
<4>[ 237.711287] x14: 0000000000000002 x13: 0000000000000000 x12: 000000000000045d
<4>[ 237.718706] x11: 0000000000000000 x10: 00000000000027d0 x9 : ffffc15095d81d18
<4>[ 237.726126] x8 : ffffc150974eb008 x7 : ffff41b15fdc2f80 x6 : ffffc15095ce58c0
<4>[ 237.733545] x5 : 0000000000000000 x4 : ffff8000098eb6f0 x3 : 0000000000000000
<4>[ 237.740963] x2 : 7ec5fa8849bae900 x1 : 7ec5fa8849bae900 x0 : 0000000000000014
<4>[ 237.748383] Call trace:
<4>[ 237.751091] lvts_device_enable_and_init+0xdc/0xf0
<4>[ 237.756155] lvts_init+0x180/0x520
<4>[ 237.759824] lvts_resume+0x1c/0x78
<4>[ 237.763494] platform_pm_resume+0x5c/0x80
<4>[ 237.767777] dpm_run_callback+0x80/0x2e8
<4>[ 237.771972] device_resume+0x90/0x1c0
<4>[ 237.775904] dpm_resume+0x110/0x470
<4>[ 237.779663] dpm_resume_end+0x20/0x38
<4>[ 237.783596] suspend_devices_and_enter+0x1e4/0xb90
<4>[ 237.788662] pm_suspend+0x270/0x318
<4>[ 237.792419] state_store+0x94/0x120
<4>[ 237.796176] kobj_attr_store+0x18/0x30
<4>[ 237.800198] sysfs_kf_write+0x54/0x80
<4>[ 237.804131] kernfs_fop_write_iter+0x128/0x1c0
<4>[ 237.808845] vfs_write+0x39c/0x510
<4>[ 237.812517] ksys_write+0x74/0x100
<4>[ 237.816187] __arm64_sys_write+0x24/0x30
<4>[ 237.820380] invoke_syscall+0x4c/0x110
<4>[ 237.824401] el0_svc_common.constprop.0+0x68/0x128
<4>[ 237.829464] do_el0_svc+0x34/0xc0
<4>[ 237.833048] el0_svc+0x4c/0xc0
<4>[ 237.836371] el0t_64_sync_handler+0xb8/0xc0
<4>[ 237.840824] el0t_64_sync+0x18c/0x190
<0>[ 237.844759] Code: 11000673 6b13001f 54fffaa8 52800280 (b9003a80)
<4>[ 237.851129] ---[ end trace 0000000000000000 ]---

Thanks,
N?colas

2022-08-05 00:15:11

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v8.1, 4/7] thermal: mediatek: Add LVTS driver for mt8192 thermal zones

Hi Balsam,

another thing.

On Thu, Aug 04, 2022 at 03:09:09PM +0200, [email protected] wrote:
[..]
> --- /dev/null
> +++ b/drivers/thermal/mediatek/lvts_thermal.h
[..]
> +#define ONE_SAMPLE (lvts_data->counting_window_us + 2 * BUS_ACCESS_US)

This macro is still using a hardcoded variable.

> +#define NUM_OF_SAMPLE(tc_id) ((lvts_data->tc[tc_id].hw_filter < LVTS_FILTER_2) ? \
> + 1 : ((lvts_data->tc[tc_id].hw_filter > LVTS_FILTER_16_OF_18) ? \
> + 1 : ((lvts_data->tc[tc_id].hw_filter == LVTS_FILTER_16_OF_18) ? \
> + 18 : ((lvts_data->tc[tc_id].hw_filter == LVTS_FILTER_8_OF_10) ? \
> + 10 : (lvts_data->tc[tc_id].hw_filter * 2)))))

Ditto.

> +
> +#define PERIOD_UNIT_US(tc_id) ((lvts_data->tc[tc_id].tc_speed->period_unit * 256 * \
> + CLOCK_26MHZ_CYCLE_NS) / 1000)
> +#define FILTER_INT_US(tc_id) (lvts_data->tc[tc_id].tc_speed->filter_interval_delay * \
> + PERIOD_UNIT_US(tc_id))
> +#define SENSOR_INT_US(tc_id) (lvts_data->tc[tc_id].tc_speed->sensor_interval_delay * \
> + PERIOD_UNIT_US(tc_id))
> +#define GROUP_INT_US(tc_id) (lvts_data->tc[tc_id].tc_speed->group_interval_delay * \
> + PERIOD_UNIT_US(tc_id))

All of these too.

> +#define SENSOR_LATENCY_US(tc_id) ((NUM_OF_SAMPLE(tc_id) - 1) * FILTER_INT_US(tc_id) + \
[..]
> +#define DEVICE_READ (CK26M_ACTIVE(lvts_data) | DEVICE_ACCESS)

And this.

> +#define DEVICE_WRITE (CK26M_ACTIVE(lvts_data) | DEVICE_ACCESS | WRITE_ACCESS)

And this.

> +#define RESET_ALL_DEVICES (DEVICE_WRITE | RG_TSFM_RST << 8 | 0xFF)
> +#define READ_DEVICE_REG(reg_id) (DEVICE_READ | (reg_id) << 8 | 0x00)
> +#define READ_BACK_DEVICE_ID (CK26M_ACTIVE(lvts_data) | DEVICE_ACCESS | BROADCAST_ID_UPDATE | \

And finally this one.

I might've have missed some, so please double-check and remove any lingering
hardcoded variable in macros.

Thanks,
N?colas

> + RG_DID_LVTS << 8)
[..]

2022-08-05 09:00:05

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v8.1, 2/7] dt-bindings: thermal: Add binding document for LVTS thermal controllers

On 05/08/2022 01:11, Nícolas F. R. A. Prado wrote:
>> 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..53c44a73f3a4
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml
> [..]
>> + nvmem-cells:
>
> minItems: 1
>
> since mt8192 only has one of them.

Then add allOf:if:then: which will constrain it per variant/compatible.

Best regards,
Krzysztof

2022-08-05 15:35:00

by Balsam CHIHI

[permalink] [raw]
Subject: Re: [PATCH v8.1, 5/7] arm64: dts: mt8195: Add efuse node to mt8195

On Thu, Aug 4, 2022 at 5:22 PM Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 04/08/2022 15:09, [email protected] wrote:
> > From: Alexandre Bailon <[email protected]>
> >
> > This adds the efuse node. This will be required by the thermal driver
> > to get the calibration data.
> >
> > Signed-off-by: Alexandre Bailon <[email protected]>
> > Signed-off-by: Balsam CHIHI <[email protected]>
> > ---
> > arch/arm64/boot/dts/mediatek/mt8195.dtsi | 14 +++++++++++++-
> > 1 file changed, 13 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> > index 73a0e2103b83..cbd0401968a2 100644
> > --- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> > +++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
> > @@ -1,6 +1,6 @@
> > // SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > /*
> > - * Copyright (c) 2021 MediaTek Inc.
> > + * Copyright (c) 2022 MediaTek Inc.
>
> Why changing first copyright release date? This was not explained in
> commit msg.
>
>
>
> Best regards,
> Krzysztof
Hello Krzysztof,

I will drop this change from this commit. There is no reason for that.
Thank you for the review.

Best regards,
Balsam