diff options
Diffstat (limited to 'drivers/thermal/mediatek')
-rw-r--r-- | drivers/thermal/mediatek/Kconfig | 37 | ||||
-rw-r--r-- | drivers/thermal/mediatek/Makefile | 2 | ||||
-rw-r--r-- | drivers/thermal/mediatek/auxadc_thermal.c | 1313 | ||||
-rw-r--r-- | drivers/thermal/mediatek/lvts_thermal.c | 1367 |
4 files changed, 2719 insertions, 0 deletions
diff --git a/drivers/thermal/mediatek/Kconfig b/drivers/thermal/mediatek/Kconfig new file mode 100644 index 0000000000..d82c86d9be --- /dev/null +++ b/drivers/thermal/mediatek/Kconfig @@ -0,0 +1,37 @@ +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. + +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 new file mode 100644 index 0000000000..1c6daa1e64 --- /dev/null +++ b/drivers/thermal/mediatek/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MTK_SOC_THERMAL) += auxadc_thermal.o +obj-$(CONFIG_MTK_LVTS_THERMAL) += lvts_thermal.o diff --git a/drivers/thermal/mediatek/auxadc_thermal.c b/drivers/thermal/mediatek/auxadc_thermal.c new file mode 100644 index 0000000000..8b0edb2048 --- /dev/null +++ b/drivers/thermal/mediatek/auxadc_thermal.c @@ -0,0 +1,1313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Hanyi Wu <hanyi.wu@mediatek.com> + * Sascha Hauer <s.hauer@pengutronix.de> + * Dawei Chien <dawei.chien@mediatek.com> + * Louis Yu <louis.yu@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.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/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/thermal.h> +#include <linux/reset.h> +#include <linux/types.h> + +#include "../thermal_hwmon.h" + +/* AUXADC Registers */ +#define AUXADC_CON1_SET_V 0x008 +#define AUXADC_CON1_CLR_V 0x00c +#define AUXADC_CON2_V 0x010 +#define AUXADC_DATA(channel) (0x14 + (channel) * 4) + +#define APMIXED_SYS_TS_CON0 0x600 +#define APMIXED_SYS_TS_CON1 0x604 + +/* Thermal Controller Registers */ +#define TEMP_MONCTL0 0x000 +#define TEMP_MONCTL1 0x004 +#define TEMP_MONCTL2 0x008 +#define TEMP_MONIDET0 0x014 +#define TEMP_MONIDET1 0x018 +#define TEMP_MSRCTL0 0x038 +#define TEMP_MSRCTL1 0x03c +#define TEMP_AHBPOLL 0x040 +#define TEMP_AHBTO 0x044 +#define TEMP_ADCPNP0 0x048 +#define TEMP_ADCPNP1 0x04c +#define TEMP_ADCPNP2 0x050 +#define TEMP_ADCPNP3 0x0b4 + +#define TEMP_ADCMUX 0x054 +#define TEMP_ADCEN 0x060 +#define TEMP_PNPMUXADDR 0x064 +#define TEMP_ADCMUXADDR 0x068 +#define TEMP_ADCENADDR 0x074 +#define TEMP_ADCVALIDADDR 0x078 +#define TEMP_ADCVOLTADDR 0x07c +#define TEMP_RDCTRL 0x080 +#define TEMP_ADCVALIDMASK 0x084 +#define TEMP_ADCVOLTAGESHIFT 0x088 +#define TEMP_ADCWRITECTRL 0x08c +#define TEMP_MSR0 0x090 +#define TEMP_MSR1 0x094 +#define TEMP_MSR2 0x098 +#define TEMP_MSR3 0x0B8 + +#define TEMP_SPARE0 0x0f0 + +#define TEMP_ADCPNP0_1 0x148 +#define TEMP_ADCPNP1_1 0x14c +#define TEMP_ADCPNP2_1 0x150 +#define TEMP_MSR0_1 0x190 +#define TEMP_MSR1_1 0x194 +#define TEMP_MSR2_1 0x198 +#define TEMP_ADCPNP3_1 0x1b4 +#define TEMP_MSR3_1 0x1B8 + +#define PTPCORESEL 0x400 + +#define TEMP_MONCTL1_PERIOD_UNIT(x) ((x) & 0x3ff) + +#define TEMP_MONCTL2_FILTER_INTERVAL(x) (((x) & 0x3ff) << 16) +#define TEMP_MONCTL2_SENSOR_INTERVAL(x) ((x) & 0x3ff) + +#define TEMP_AHBPOLL_ADC_POLL_INTERVAL(x) (x) + +#define TEMP_ADCWRITECTRL_ADC_PNP_WRITE BIT(0) +#define TEMP_ADCWRITECTRL_ADC_MUX_WRITE BIT(1) + +#define TEMP_ADCVALIDMASK_VALID_HIGH BIT(5) +#define TEMP_ADCVALIDMASK_VALID_POS(bit) (bit) + +/* MT8173 thermal sensors */ +#define MT8173_TS1 0 +#define MT8173_TS2 1 +#define MT8173_TS3 2 +#define MT8173_TS4 3 +#define MT8173_TSABB 4 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT8173_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT8173 */ +#define MT8173_NUM_SENSORS 5 + +/* The number of banks in the MT8173 */ +#define MT8173_NUM_ZONES 4 + +/* The number of sensing points per bank */ +#define MT8173_NUM_SENSORS_PER_ZONE 4 + +/* The number of controller in the MT8173 */ +#define MT8173_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT8173_CALIBRATION 165 + +/* Valid temperatures range */ +#define MT8173_TEMP_MIN -20000 +#define MT8173_TEMP_MAX 150000 + +/* + * Layout of the fuses providing the calibration data + * These macros could be used for MT8183, MT8173, MT2701, and MT2712. + * MT8183 has 6 sensors and needs 6 VTS calibration data. + * MT8173 has 5 sensors and needs 5 VTS calibration data. + * MT2701 has 3 sensors and needs 3 VTS calibration data. + * MT2712 has 4 sensors and needs 4 VTS calibration data. + */ +#define CALIB_BUF0_VALID_V1 BIT(0) +#define CALIB_BUF1_ADC_GE_V1(x) (((x) >> 22) & 0x3ff) +#define CALIB_BUF0_VTS_TS1_V1(x) (((x) >> 17) & 0x1ff) +#define CALIB_BUF0_VTS_TS2_V1(x) (((x) >> 8) & 0x1ff) +#define CALIB_BUF1_VTS_TS3_V1(x) (((x) >> 0) & 0x1ff) +#define CALIB_BUF2_VTS_TS4_V1(x) (((x) >> 23) & 0x1ff) +#define CALIB_BUF2_VTS_TS5_V1(x) (((x) >> 5) & 0x1ff) +#define CALIB_BUF2_VTS_TSABB_V1(x) (((x) >> 14) & 0x1ff) +#define CALIB_BUF0_DEGC_CALI_V1(x) (((x) >> 1) & 0x3f) +#define CALIB_BUF0_O_SLOPE_V1(x) (((x) >> 26) & 0x3f) +#define CALIB_BUF0_O_SLOPE_SIGN_V1(x) (((x) >> 7) & 0x1) +#define CALIB_BUF1_ID_V1(x) (((x) >> 9) & 0x1) + +/* + * Layout of the fuses providing the calibration data + * These macros could be used for MT7622. + */ +#define CALIB_BUF0_ADC_OE_V2(x) (((x) >> 22) & 0x3ff) +#define CALIB_BUF0_ADC_GE_V2(x) (((x) >> 12) & 0x3ff) +#define CALIB_BUF0_DEGC_CALI_V2(x) (((x) >> 6) & 0x3f) +#define CALIB_BUF0_O_SLOPE_V2(x) (((x) >> 0) & 0x3f) +#define CALIB_BUF1_VTS_TS1_V2(x) (((x) >> 23) & 0x1ff) +#define CALIB_BUF1_VTS_TS2_V2(x) (((x) >> 14) & 0x1ff) +#define CALIB_BUF1_VTS_TSABB_V2(x) (((x) >> 5) & 0x1ff) +#define CALIB_BUF1_VALID_V2(x) (((x) >> 4) & 0x1) +#define CALIB_BUF1_O_SLOPE_SIGN_V2(x) (((x) >> 3) & 0x1) + +/* + * Layout of the fuses providing the calibration data + * These macros can be used for MT7981 and MT7986. + */ +#define CALIB_BUF0_ADC_GE_V3(x) (((x) >> 0) & 0x3ff) +#define CALIB_BUF0_DEGC_CALI_V3(x) (((x) >> 20) & 0x3f) +#define CALIB_BUF0_O_SLOPE_V3(x) (((x) >> 26) & 0x3f) +#define CALIB_BUF1_VTS_TS1_V3(x) (((x) >> 0) & 0x1ff) +#define CALIB_BUF1_VTS_TS2_V3(x) (((x) >> 21) & 0x1ff) +#define CALIB_BUF1_VTS_TSABB_V3(x) (((x) >> 9) & 0x1ff) +#define CALIB_BUF1_VALID_V3(x) (((x) >> 18) & 0x1) +#define CALIB_BUF1_O_SLOPE_SIGN_V3(x) (((x) >> 19) & 0x1) +#define CALIB_BUF1_ID_V3(x) (((x) >> 20) & 0x1) + +enum { + VTS1, + VTS2, + VTS3, + VTS4, + VTS5, + VTSABB, + MAX_NUM_VTS, +}; + +enum mtk_thermal_version { + MTK_THERMAL_V1 = 1, + MTK_THERMAL_V2, + MTK_THERMAL_V3, +}; + +/* MT2701 thermal sensors */ +#define MT2701_TS1 0 +#define MT2701_TS2 1 +#define MT2701_TSABB 2 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT2701_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT2701 */ +#define MT2701_NUM_SENSORS 3 + +/* The number of sensing points per bank */ +#define MT2701_NUM_SENSORS_PER_ZONE 3 + +/* The number of controller in the MT2701 */ +#define MT2701_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT2701_CALIBRATION 165 + +/* MT2712 thermal sensors */ +#define MT2712_TS1 0 +#define MT2712_TS2 1 +#define MT2712_TS3 2 +#define MT2712_TS4 3 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT2712_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT2712 */ +#define MT2712_NUM_SENSORS 4 + +/* The number of sensing points per bank */ +#define MT2712_NUM_SENSORS_PER_ZONE 4 + +/* The number of controller in the MT2712 */ +#define MT2712_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT2712_CALIBRATION 165 + +#define MT7622_TEMP_AUXADC_CHANNEL 11 +#define MT7622_NUM_SENSORS 1 +#define MT7622_NUM_ZONES 1 +#define MT7622_NUM_SENSORS_PER_ZONE 1 +#define MT7622_TS1 0 +#define MT7622_NUM_CONTROLLER 1 + +/* The maximum number of banks */ +#define MAX_NUM_ZONES 8 + +/* The calibration coefficient of sensor */ +#define MT7622_CALIBRATION 165 + +/* MT8183 thermal sensors */ +#define MT8183_TS1 0 +#define MT8183_TS2 1 +#define MT8183_TS3 2 +#define MT8183_TS4 3 +#define MT8183_TS5 4 +#define MT8183_TSABB 5 + +/* AUXADC channel is used for the temperature sensors */ +#define MT8183_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT8183 */ +#define MT8183_NUM_SENSORS 6 + +/* The number of banks in the MT8183 */ +#define MT8183_NUM_ZONES 1 + +/* The number of sensing points per bank */ +#define MT8183_NUM_SENSORS_PER_ZONE 6 + +/* The number of controller in the MT8183 */ +#define MT8183_NUM_CONTROLLER 2 + +/* The calibration coefficient of sensor */ +#define MT8183_CALIBRATION 153 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT7986_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT7986 */ +#define MT7986_NUM_SENSORS 1 + +/* The number of banks in the MT7986 */ +#define MT7986_NUM_ZONES 1 + +/* The number of sensing points per bank */ +#define MT7986_NUM_SENSORS_PER_ZONE 1 + +/* MT7986 thermal sensors */ +#define MT7986_TS1 0 + +/* The number of controller in the MT7986 */ +#define MT7986_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT7986_CALIBRATION 165 + +/* MT8365 */ +#define MT8365_TEMP_AUXADC_CHANNEL 11 +#define MT8365_CALIBRATION 164 +#define MT8365_NUM_CONTROLLER 1 +#define MT8365_NUM_BANKS 1 +#define MT8365_NUM_SENSORS 3 +#define MT8365_NUM_SENSORS_PER_ZONE 3 +#define MT8365_TS1 0 +#define MT8365_TS2 1 +#define MT8365_TS3 2 + +struct mtk_thermal; + +struct thermal_bank_cfg { + unsigned int num_sensors; + const int *sensors; +}; + +struct mtk_thermal_bank { + struct mtk_thermal *mt; + int id; +}; + +struct mtk_thermal_data { + s32 num_banks; + s32 num_sensors; + s32 auxadc_channel; + const int *vts_index; + const int *sensor_mux_values; + const int *msr; + const int *adcpnp; + const int cali_val; + const int num_controller; + const int *controller_offset; + bool need_switch_bank; + struct thermal_bank_cfg bank_data[MAX_NUM_ZONES]; + enum mtk_thermal_version version; + u32 apmixed_buffer_ctl_reg; + u32 apmixed_buffer_ctl_mask; + u32 apmixed_buffer_ctl_set; +}; + +struct mtk_thermal { + struct device *dev; + void __iomem *thermal_base; + + struct clk *clk_peri_therm; + struct clk *clk_auxadc; + /* lock: for getting and putting banks */ + struct mutex lock; + + /* Calibration values */ + s32 adc_ge; + s32 adc_oe; + s32 degc_cali; + s32 o_slope; + s32 o_slope_sign; + s32 vts[MAX_NUM_VTS]; + + const struct mtk_thermal_data *conf; + struct mtk_thermal_bank banks[MAX_NUM_ZONES]; + + int (*raw_to_mcelsius)(struct mtk_thermal *mt, int sensno, s32 raw); +}; + +/* MT8183 thermal sensor data */ +static const int mt8183_bank_data[MT8183_NUM_SENSORS] = { + MT8183_TS1, MT8183_TS2, MT8183_TS3, MT8183_TS4, MT8183_TS5, MT8183_TSABB +}; + +static const int mt8183_msr[MT8183_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0_1, TEMP_MSR1_1, TEMP_MSR2_1, TEMP_MSR1, TEMP_MSR0, TEMP_MSR3_1 +}; + +static const int mt8183_adcpnp[MT8183_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0_1, TEMP_ADCPNP1_1, TEMP_ADCPNP2_1, + TEMP_ADCPNP1, TEMP_ADCPNP0, TEMP_ADCPNP3_1 +}; + +static const int mt8183_mux_values[MT8183_NUM_SENSORS] = { 0, 1, 2, 3, 4, 0 }; +static const int mt8183_tc_offset[MT8183_NUM_CONTROLLER] = {0x0, 0x100}; + +static const int mt8183_vts_index[MT8183_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4, VTS5, VTSABB +}; + +/* MT8173 thermal sensor data */ +static const int mt8173_bank_data[MT8173_NUM_ZONES][3] = { + { MT8173_TS2, MT8173_TS3 }, + { MT8173_TS2, MT8173_TS4 }, + { MT8173_TS1, MT8173_TS2, MT8173_TSABB }, + { MT8173_TS2 }, +}; + +static const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3 +}; + +static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 +}; + +static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; +static const int mt8173_tc_offset[MT8173_NUM_CONTROLLER] = { 0x0, }; + +static const int mt8173_vts_index[MT8173_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4, VTSABB +}; + +/* MT2701 thermal sensor data */ +static const int mt2701_bank_data[MT2701_NUM_SENSORS] = { + MT2701_TS1, MT2701_TS2, MT2701_TSABB +}; + +static const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2 +}; + +static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2 +}; + +static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; +static const int mt2701_tc_offset[MT2701_NUM_CONTROLLER] = { 0x0, }; + +static const int mt2701_vts_index[MT2701_NUM_SENSORS] = { + VTS1, VTS2, VTS3 +}; + +/* MT2712 thermal sensor data */ +static const int mt2712_bank_data[MT2712_NUM_SENSORS] = { + MT2712_TS1, MT2712_TS2, MT2712_TS3, MT2712_TS4 +}; + +static const int mt2712_msr[MT2712_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3 +}; + +static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 +}; + +static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 }; +static const int mt2712_tc_offset[MT2712_NUM_CONTROLLER] = { 0x0, }; + +static const int mt2712_vts_index[MT2712_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4 +}; + +/* MT7622 thermal sensor data */ +static const int mt7622_bank_data[MT7622_NUM_SENSORS] = { MT7622_TS1, }; +static const int mt7622_msr[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; +static const int mt7622_adcpnp[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; +static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, }; +static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 }; +static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, }; + +/* MT7986 thermal sensor data */ +static const int mt7986_bank_data[MT7986_NUM_SENSORS] = { MT7986_TS1, }; +static const int mt7986_msr[MT7986_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; +static const int mt7986_adcpnp[MT7986_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; +static const int mt7986_mux_values[MT7986_NUM_SENSORS] = { 0, }; +static const int mt7986_vts_index[MT7986_NUM_SENSORS] = { VTS1 }; +static const int mt7986_tc_offset[MT7986_NUM_CONTROLLER] = { 0x0, }; + +/* MT8365 thermal sensor data */ +static const int mt8365_bank_data[MT8365_NUM_SENSORS] = { + MT8365_TS1, MT8365_TS2, MT8365_TS3 +}; + +static const int mt8365_msr[MT8365_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2 +}; + +static const int mt8365_adcpnp[MT8365_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2 +}; + +static const int mt8365_mux_values[MT8365_NUM_SENSORS] = { 0, 1, 2 }; +static const int mt8365_tc_offset[MT8365_NUM_CONTROLLER] = { 0 }; + +static const int mt8365_vts_index[MT8365_NUM_SENSORS] = { VTS1, VTS2, VTS3 }; + +/* + * The MT8173 thermal controller has four banks. Each bank can read up to + * four temperature sensors simultaneously. The MT8173 has a total of 5 + * temperature sensors. We use each bank to measure a certain area of the + * SoC. Since TS2 is located centrally in the SoC it is influenced by multiple + * areas, hence is used in different banks. + * + * The thermal core only gets the maximum temperature of all banks, so + * the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data, and this indeed needs the temperatures of the individual banks + * for making better decisions. + */ +static const struct mtk_thermal_data mt8173_thermal_data = { + .auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL, + .num_banks = MT8173_NUM_ZONES, + .num_sensors = MT8173_NUM_SENSORS, + .vts_index = mt8173_vts_index, + .cali_val = MT8173_CALIBRATION, + .num_controller = MT8173_NUM_CONTROLLER, + .controller_offset = mt8173_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 2, + .sensors = mt8173_bank_data[0], + }, { + .num_sensors = 2, + .sensors = mt8173_bank_data[1], + }, { + .num_sensors = 3, + .sensors = mt8173_bank_data[2], + }, { + .num_sensors = 1, + .sensors = mt8173_bank_data[3], + }, + }, + .msr = mt8173_msr, + .adcpnp = mt8173_adcpnp, + .sensor_mux_values = mt8173_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * The MT2701 thermal controller has one bank, which can read up to + * three temperature sensors simultaneously. The MT2701 has a total of 3 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt2701_thermal_data = { + .auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL, + .num_banks = 1, + .num_sensors = MT2701_NUM_SENSORS, + .vts_index = mt2701_vts_index, + .cali_val = MT2701_CALIBRATION, + .num_controller = MT2701_NUM_CONTROLLER, + .controller_offset = mt2701_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 3, + .sensors = mt2701_bank_data, + }, + }, + .msr = mt2701_msr, + .adcpnp = mt2701_adcpnp, + .sensor_mux_values = mt2701_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * The MT8365 thermal controller has one bank, which can read up to + * four temperature sensors simultaneously. The MT8365 has a total of 3 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt8365_thermal_data = { + .auxadc_channel = MT8365_TEMP_AUXADC_CHANNEL, + .num_banks = MT8365_NUM_BANKS, + .num_sensors = MT8365_NUM_SENSORS, + .vts_index = mt8365_vts_index, + .cali_val = MT8365_CALIBRATION, + .num_controller = MT8365_NUM_CONTROLLER, + .controller_offset = mt8365_tc_offset, + .need_switch_bank = false, + .bank_data = { + { + .num_sensors = MT8365_NUM_SENSORS, + .sensors = mt8365_bank_data + }, + }, + .msr = mt8365_msr, + .adcpnp = mt8365_adcpnp, + .sensor_mux_values = mt8365_mux_values, + .version = MTK_THERMAL_V1, + .apmixed_buffer_ctl_reg = APMIXED_SYS_TS_CON0, + .apmixed_buffer_ctl_mask = (u32) ~GENMASK(29, 28), + .apmixed_buffer_ctl_set = 0, +}; + +/* + * The MT2712 thermal controller has one bank, which can read up to + * four temperature sensors simultaneously. The MT2712 has a total of 4 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt2712_thermal_data = { + .auxadc_channel = MT2712_TEMP_AUXADC_CHANNEL, + .num_banks = 1, + .num_sensors = MT2712_NUM_SENSORS, + .vts_index = mt2712_vts_index, + .cali_val = MT2712_CALIBRATION, + .num_controller = MT2712_NUM_CONTROLLER, + .controller_offset = mt2712_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 4, + .sensors = mt2712_bank_data, + }, + }, + .msr = mt2712_msr, + .adcpnp = mt2712_adcpnp, + .sensor_mux_values = mt2712_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * MT7622 have only one sensing point which uses AUXADC Channel 11 for raw data + * access. + */ +static const struct mtk_thermal_data mt7622_thermal_data = { + .auxadc_channel = MT7622_TEMP_AUXADC_CHANNEL, + .num_banks = MT7622_NUM_ZONES, + .num_sensors = MT7622_NUM_SENSORS, + .vts_index = mt7622_vts_index, + .cali_val = MT7622_CALIBRATION, + .num_controller = MT7622_NUM_CONTROLLER, + .controller_offset = mt7622_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 1, + .sensors = mt7622_bank_data, + }, + }, + .msr = mt7622_msr, + .adcpnp = mt7622_adcpnp, + .sensor_mux_values = mt7622_mux_values, + .version = MTK_THERMAL_V2, + .apmixed_buffer_ctl_reg = APMIXED_SYS_TS_CON1, + .apmixed_buffer_ctl_mask = GENMASK(31, 6) | BIT(3), + .apmixed_buffer_ctl_set = BIT(0), +}; + +/* + * The MT8183 thermal controller has one bank for the current SW framework. + * The MT8183 has a total of 6 temperature sensors. + * There are two thermal controller to control the six sensor. + * The first one bind 2 sensor, and the other bind 4 sensors. + * The thermal core only gets the maximum temperature of all sensor, so + * the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data, and this indeed needs the temperatures of the individual banks + * for making better decisions. + */ +static const struct mtk_thermal_data mt8183_thermal_data = { + .auxadc_channel = MT8183_TEMP_AUXADC_CHANNEL, + .num_banks = MT8183_NUM_ZONES, + .num_sensors = MT8183_NUM_SENSORS, + .vts_index = mt8183_vts_index, + .cali_val = MT8183_CALIBRATION, + .num_controller = MT8183_NUM_CONTROLLER, + .controller_offset = mt8183_tc_offset, + .need_switch_bank = false, + .bank_data = { + { + .num_sensors = 6, + .sensors = mt8183_bank_data, + }, + }, + + .msr = mt8183_msr, + .adcpnp = mt8183_adcpnp, + .sensor_mux_values = mt8183_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * MT7986 uses AUXADC Channel 11 for raw data access. + */ +static const struct mtk_thermal_data mt7986_thermal_data = { + .auxadc_channel = MT7986_TEMP_AUXADC_CHANNEL, + .num_banks = MT7986_NUM_ZONES, + .num_sensors = MT7986_NUM_SENSORS, + .vts_index = mt7986_vts_index, + .cali_val = MT7986_CALIBRATION, + .num_controller = MT7986_NUM_CONTROLLER, + .controller_offset = mt7986_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 1, + .sensors = mt7986_bank_data, + }, + }, + .msr = mt7986_msr, + .adcpnp = mt7986_adcpnp, + .sensor_mux_values = mt7986_mux_values, + .version = MTK_THERMAL_V3, +}; + +static bool mtk_thermal_temp_is_valid(int temp) +{ + return (temp >= MT8173_TEMP_MIN) && (temp <= MT8173_TEMP_MAX); +} + +/** + * raw_to_mcelsius_v1 - convert a raw ADC value to mcelsius + * @mt: The thermal controller + * @sensno: sensor number + * @raw: raw ADC value + * + * This converts the raw ADC value to mcelsius using the SoC specific + * calibration constants + */ +static int raw_to_mcelsius_v1(struct mtk_thermal *mt, int sensno, s32 raw) +{ + s32 tmp; + + raw &= 0xfff; + + tmp = 203450520 << 3; + tmp /= mt->conf->cali_val + mt->o_slope; + tmp /= 10000 + mt->adc_ge; + tmp *= raw - mt->vts[sensno] - 3350; + tmp >>= 3; + + return mt->degc_cali * 500 - tmp; +} + +static int raw_to_mcelsius_v2(struct mtk_thermal *mt, int sensno, s32 raw) +{ + s32 format_1; + s32 format_2; + s32 g_oe; + s32 g_gain; + s32 g_x_roomt; + s32 tmp; + + if (raw == 0) + return 0; + + raw &= 0xfff; + g_gain = 10000 + (((mt->adc_ge - 512) * 10000) >> 12); + g_oe = mt->adc_oe - 512; + format_1 = mt->vts[VTS2] + 3105 - g_oe; + format_2 = (mt->degc_cali * 10) >> 1; + g_x_roomt = (((format_1 * 10000) >> 12) * 10000) / g_gain; + + tmp = (((((raw - g_oe) * 10000) >> 12) * 10000) / g_gain) - g_x_roomt; + tmp = tmp * 10 * 100 / 11; + + if (mt->o_slope_sign == 0) + tmp = tmp / (165 - mt->o_slope); + else + tmp = tmp / (165 + mt->o_slope); + + return (format_2 - tmp) * 100; +} + +static int raw_to_mcelsius_v3(struct mtk_thermal *mt, int sensno, s32 raw) +{ + s32 tmp; + + if (raw == 0) + return 0; + + raw &= 0xfff; + tmp = 100000 * 15 / 16 * 10000; + tmp /= 4096 - 512 + mt->adc_ge; + tmp /= 1490; + tmp *= raw - mt->vts[sensno] - 2900; + + return mt->degc_cali * 500 - tmp; +} + +/** + * mtk_thermal_get_bank - get bank + * @bank: The bank + * + * The bank registers are banked, we have to select a bank in the + * PTPCORESEL register to access it. + */ +static void mtk_thermal_get_bank(struct mtk_thermal_bank *bank) +{ + struct mtk_thermal *mt = bank->mt; + u32 val; + + if (mt->conf->need_switch_bank) { + mutex_lock(&mt->lock); + + val = readl(mt->thermal_base + PTPCORESEL); + val &= ~0xf; + val |= bank->id; + writel(val, mt->thermal_base + PTPCORESEL); + } +} + +/** + * mtk_thermal_put_bank - release bank + * @bank: The bank + * + * release a bank previously taken with mtk_thermal_get_bank, + */ +static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank) +{ + struct mtk_thermal *mt = bank->mt; + + if (mt->conf->need_switch_bank) + mutex_unlock(&mt->lock); +} + +/** + * mtk_thermal_bank_temperature - get the temperature of a bank + * @bank: The bank + * + * The temperature of a bank is considered the maximum temperature of + * the sensors associated to the bank. + */ +static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank) +{ + struct mtk_thermal *mt = bank->mt; + const struct mtk_thermal_data *conf = mt->conf; + int i, temp = INT_MIN, max = INT_MIN; + u32 raw; + + for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) { + raw = readl(mt->thermal_base + conf->msr[i]); + + temp = mt->raw_to_mcelsius( + mt, conf->bank_data[bank->id].sensors[i], raw); + + /* + * Depending on the filt/sen intervals and ADC polling time, + * we may need up to 60 milliseconds after initialization: this + * will result in the first reading containing an out of range + * temperature value. + * Validate the reading to both address the aforementioned issue + * and to eventually avoid bogus readings during runtime in the + * event that the AUXADC gets unstable due to high EMI, etc. + */ + if (!mtk_thermal_temp_is_valid(temp)) + temp = THERMAL_TEMP_INVALID; + + if (temp > max) + max = temp; + } + + return max; +} + +static int mtk_read_temp(struct thermal_zone_device *tz, int *temperature) +{ + struct mtk_thermal *mt = thermal_zone_device_priv(tz); + int i; + int tempmax = INT_MIN; + + for (i = 0; i < mt->conf->num_banks; i++) { + struct mtk_thermal_bank *bank = &mt->banks[i]; + + mtk_thermal_get_bank(bank); + + tempmax = max(tempmax, mtk_thermal_bank_temperature(bank)); + + mtk_thermal_put_bank(bank); + } + + *temperature = tempmax; + + return 0; +} + +static const struct thermal_zone_device_ops mtk_thermal_ops = { + .get_temp = mtk_read_temp, +}; + +static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, + u32 apmixed_phys_base, u32 auxadc_phys_base, + int ctrl_id) +{ + struct mtk_thermal_bank *bank = &mt->banks[num]; + const struct mtk_thermal_data *conf = mt->conf; + int i; + + int offset = mt->conf->controller_offset[ctrl_id]; + void __iomem *controller_base = mt->thermal_base + offset; + + bank->id = num; + bank->mt = mt; + + mtk_thermal_get_bank(bank); + + /* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us */ + writel(TEMP_MONCTL1_PERIOD_UNIT(12), controller_base + TEMP_MONCTL1); + + /* + * filt interval is 1 * 46.540us = 46.54us, + * sen interval is 429 * 46.540us = 19.96ms + */ + writel(TEMP_MONCTL2_FILTER_INTERVAL(1) | + TEMP_MONCTL2_SENSOR_INTERVAL(429), + controller_base + TEMP_MONCTL2); + + /* poll is set to 10u */ + writel(TEMP_AHBPOLL_ADC_POLL_INTERVAL(768), + controller_base + TEMP_AHBPOLL); + + /* temperature sampling control, 1 sample */ + writel(0x0, controller_base + TEMP_MSRCTL0); + + /* exceed this polling time, IRQ would be inserted */ + writel(0xffffffff, controller_base + TEMP_AHBTO); + + /* number of interrupts per event, 1 is enough */ + writel(0x0, controller_base + TEMP_MONIDET0); + writel(0x0, controller_base + TEMP_MONIDET1); + + /* + * The MT8173 thermal controller does not have its own ADC. Instead it + * uses AHB bus accesses to control the AUXADC. To do this the thermal + * controller has to be programmed with the physical addresses of the + * AUXADC registers and with the various bit positions in the AUXADC. + * Also the thermal controller controls a mux in the APMIXEDSYS register + * space. + */ + + /* + * this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0) + * automatically by hw + */ + writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCMUX); + + /* AHB address for auxadc mux selection */ + writel(auxadc_phys_base + AUXADC_CON1_CLR_V, + controller_base + TEMP_ADCMUXADDR); + + if (mt->conf->version == MTK_THERMAL_V1) { + /* AHB address for pnp sensor mux selection */ + writel(apmixed_phys_base + APMIXED_SYS_TS_CON1, + controller_base + TEMP_PNPMUXADDR); + } + + /* AHB value for auxadc enable */ + writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCEN); + + /* AHB address for auxadc enable (channel 0 immediate mode selected) */ + writel(auxadc_phys_base + AUXADC_CON1_SET_V, + controller_base + TEMP_ADCENADDR); + + /* AHB address for auxadc valid bit */ + writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), + controller_base + TEMP_ADCVALIDADDR); + + /* AHB address for auxadc voltage output */ + writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), + controller_base + TEMP_ADCVOLTADDR); + + /* read valid & voltage are at the same register */ + writel(0x0, controller_base + TEMP_RDCTRL); + + /* indicate where the valid bit is */ + writel(TEMP_ADCVALIDMASK_VALID_HIGH | TEMP_ADCVALIDMASK_VALID_POS(12), + controller_base + TEMP_ADCVALIDMASK); + + /* no shift */ + writel(0x0, controller_base + TEMP_ADCVOLTAGESHIFT); + + /* enable auxadc mux write transaction */ + writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE, + controller_base + TEMP_ADCWRITECTRL); + + for (i = 0; i < conf->bank_data[num].num_sensors; i++) + writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]], + mt->thermal_base + conf->adcpnp[i]); + + writel((1 << conf->bank_data[num].num_sensors) - 1, + controller_base + TEMP_MONCTL0); + + writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE | + TEMP_ADCWRITECTRL_ADC_MUX_WRITE, + controller_base + TEMP_ADCWRITECTRL); + + mtk_thermal_put_bank(bank); +} + +static u64 of_get_phys_base(struct device_node *np) +{ + struct resource res; + + if (of_address_to_resource(np, 0, &res)) + return OF_BAD_ADDR; + + return res.start; +} + +static int mtk_thermal_extract_efuse_v1(struct mtk_thermal *mt, u32 *buf) +{ + int i; + + if (!(buf[0] & CALIB_BUF0_VALID_V1)) + return -EINVAL; + + mt->adc_ge = CALIB_BUF1_ADC_GE_V1(buf[1]); + + for (i = 0; i < mt->conf->num_sensors; i++) { + switch (mt->conf->vts_index[i]) { + case VTS1: + mt->vts[VTS1] = CALIB_BUF0_VTS_TS1_V1(buf[0]); + break; + case VTS2: + mt->vts[VTS2] = CALIB_BUF0_VTS_TS2_V1(buf[0]); + break; + case VTS3: + mt->vts[VTS3] = CALIB_BUF1_VTS_TS3_V1(buf[1]); + break; + case VTS4: + mt->vts[VTS4] = CALIB_BUF2_VTS_TS4_V1(buf[2]); + break; + case VTS5: + mt->vts[VTS5] = CALIB_BUF2_VTS_TS5_V1(buf[2]); + break; + case VTSABB: + mt->vts[VTSABB] = + CALIB_BUF2_VTS_TSABB_V1(buf[2]); + break; + default: + break; + } + } + + mt->degc_cali = CALIB_BUF0_DEGC_CALI_V1(buf[0]); + if (CALIB_BUF1_ID_V1(buf[1]) & + CALIB_BUF0_O_SLOPE_SIGN_V1(buf[0])) + mt->o_slope = -CALIB_BUF0_O_SLOPE_V1(buf[0]); + else + mt->o_slope = CALIB_BUF0_O_SLOPE_V1(buf[0]); + + return 0; +} + +static int mtk_thermal_extract_efuse_v2(struct mtk_thermal *mt, u32 *buf) +{ + if (!CALIB_BUF1_VALID_V2(buf[1])) + return -EINVAL; + + mt->adc_oe = CALIB_BUF0_ADC_OE_V2(buf[0]); + mt->adc_ge = CALIB_BUF0_ADC_GE_V2(buf[0]); + mt->degc_cali = CALIB_BUF0_DEGC_CALI_V2(buf[0]); + mt->o_slope = CALIB_BUF0_O_SLOPE_V2(buf[0]); + mt->vts[VTS1] = CALIB_BUF1_VTS_TS1_V2(buf[1]); + mt->vts[VTS2] = CALIB_BUF1_VTS_TS2_V2(buf[1]); + mt->vts[VTSABB] = CALIB_BUF1_VTS_TSABB_V2(buf[1]); + mt->o_slope_sign = CALIB_BUF1_O_SLOPE_SIGN_V2(buf[1]); + + return 0; +} + +static int mtk_thermal_extract_efuse_v3(struct mtk_thermal *mt, u32 *buf) +{ + if (!CALIB_BUF1_VALID_V3(buf[1])) + return -EINVAL; + + mt->adc_ge = CALIB_BUF0_ADC_GE_V3(buf[0]); + mt->degc_cali = CALIB_BUF0_DEGC_CALI_V3(buf[0]); + mt->o_slope = CALIB_BUF0_O_SLOPE_V3(buf[0]); + mt->vts[VTS1] = CALIB_BUF1_VTS_TS1_V3(buf[1]); + mt->vts[VTS2] = CALIB_BUF1_VTS_TS2_V3(buf[1]); + mt->vts[VTSABB] = CALIB_BUF1_VTS_TSABB_V3(buf[1]); + mt->o_slope_sign = CALIB_BUF1_O_SLOPE_SIGN_V3(buf[1]); + + if (CALIB_BUF1_ID_V3(buf[1]) == 0) + mt->o_slope = 0; + + return 0; +} + +static int mtk_thermal_get_calibration_data(struct device *dev, + struct mtk_thermal *mt) +{ + struct nvmem_cell *cell; + u32 *buf; + size_t len; + int i, ret = 0; + + /* Start with default values */ + mt->adc_ge = 512; + mt->adc_oe = 512; + for (i = 0; i < mt->conf->num_sensors; i++) + mt->vts[i] = 260; + mt->degc_cali = 40; + mt->o_slope = 0; + + cell = nvmem_cell_get(dev, "calibration-data"); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return PTR_ERR(cell); + return 0; + } + + buf = (u32 *)nvmem_cell_read(cell, &len); + + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + if (len < 3 * sizeof(u32)) { + dev_warn(dev, "invalid calibration data\n"); + ret = -EINVAL; + goto out; + } + + switch (mt->conf->version) { + case MTK_THERMAL_V1: + ret = mtk_thermal_extract_efuse_v1(mt, buf); + break; + case MTK_THERMAL_V2: + ret = mtk_thermal_extract_efuse_v2(mt, buf); + break; + case MTK_THERMAL_V3: + ret = mtk_thermal_extract_efuse_v3(mt, buf); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) { + dev_info(dev, "Device not calibrated, using default calibration values\n"); + ret = 0; + } + +out: + kfree(buf); + + return ret; +} + +static const struct of_device_id mtk_thermal_of_match[] = { + { + .compatible = "mediatek,mt8173-thermal", + .data = (void *)&mt8173_thermal_data, + }, + { + .compatible = "mediatek,mt2701-thermal", + .data = (void *)&mt2701_thermal_data, + }, + { + .compatible = "mediatek,mt2712-thermal", + .data = (void *)&mt2712_thermal_data, + }, + { + .compatible = "mediatek,mt7622-thermal", + .data = (void *)&mt7622_thermal_data, + }, + { + .compatible = "mediatek,mt7986-thermal", + .data = (void *)&mt7986_thermal_data, + }, + { + .compatible = "mediatek,mt8183-thermal", + .data = (void *)&mt8183_thermal_data, + }, + { + .compatible = "mediatek,mt8365-thermal", + .data = (void *)&mt8365_thermal_data, + }, { + }, +}; +MODULE_DEVICE_TABLE(of, mtk_thermal_of_match); + +static void mtk_thermal_turn_on_buffer(struct mtk_thermal *mt, + void __iomem *apmixed_base) +{ + u32 tmp; + + if (!mt->conf->apmixed_buffer_ctl_reg) + return; + + tmp = readl(apmixed_base + mt->conf->apmixed_buffer_ctl_reg); + tmp &= mt->conf->apmixed_buffer_ctl_mask; + tmp |= mt->conf->apmixed_buffer_ctl_set; + writel(tmp, apmixed_base + mt->conf->apmixed_buffer_ctl_reg); + udelay(200); +} + +static void mtk_thermal_release_periodic_ts(struct mtk_thermal *mt, + void __iomem *auxadc_base) +{ + int tmp; + + writel(0x800, auxadc_base + AUXADC_CON1_SET_V); + writel(0x1, mt->thermal_base + TEMP_MONCTL0); + tmp = readl(mt->thermal_base + TEMP_MSRCTL1); + writel((tmp & (~0x10e)), mt->thermal_base + TEMP_MSRCTL1); +} + +static int mtk_thermal_probe(struct platform_device *pdev) +{ + int ret, i, ctrl_id; + struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node; + struct mtk_thermal *mt; + u64 auxadc_phys_base, apmixed_phys_base; + struct thermal_zone_device *tzdev; + void __iomem *apmixed_base, *auxadc_base; + + mt = devm_kzalloc(&pdev->dev, sizeof(*mt), GFP_KERNEL); + if (!mt) + return -ENOMEM; + + mt->conf = of_device_get_match_data(&pdev->dev); + + mt->thermal_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(mt->thermal_base)) + return PTR_ERR(mt->thermal_base); + + ret = mtk_thermal_get_calibration_data(&pdev->dev, mt); + if (ret) + return ret; + + mutex_init(&mt->lock); + + mt->dev = &pdev->dev; + + auxadc = of_parse_phandle(np, "mediatek,auxadc", 0); + if (!auxadc) { + dev_err(&pdev->dev, "missing auxadc node\n"); + return -ENODEV; + } + + auxadc_base = of_iomap(auxadc, 0); + auxadc_phys_base = of_get_phys_base(auxadc); + + of_node_put(auxadc); + + if (auxadc_phys_base == OF_BAD_ADDR) { + dev_err(&pdev->dev, "Can't get auxadc phys address\n"); + return -EINVAL; + } + + apmixedsys = of_parse_phandle(np, "mediatek,apmixedsys", 0); + if (!apmixedsys) { + dev_err(&pdev->dev, "missing apmixedsys node\n"); + return -ENODEV; + } + + apmixed_base = of_iomap(apmixedsys, 0); + apmixed_phys_base = of_get_phys_base(apmixedsys); + + of_node_put(apmixedsys); + + if (apmixed_phys_base == OF_BAD_ADDR) { + dev_err(&pdev->dev, "Can't get auxadc phys address\n"); + return -EINVAL; + } + + ret = device_reset_optional(&pdev->dev); + if (ret) + return ret; + + mt->clk_auxadc = devm_clk_get_enabled(&pdev->dev, "auxadc"); + if (IS_ERR(mt->clk_auxadc)) { + ret = PTR_ERR(mt->clk_auxadc); + dev_err(&pdev->dev, "Can't enable auxadc clk: %d\n", ret); + return ret; + } + + mt->clk_peri_therm = devm_clk_get_enabled(&pdev->dev, "therm"); + if (IS_ERR(mt->clk_peri_therm)) { + ret = PTR_ERR(mt->clk_peri_therm); + dev_err(&pdev->dev, "Can't enable peri clk: %d\n", ret); + return ret; + } + + mtk_thermal_turn_on_buffer(mt, apmixed_base); + + if (mt->conf->version != MTK_THERMAL_V1) + mtk_thermal_release_periodic_ts(mt, auxadc_base); + + if (mt->conf->version == MTK_THERMAL_V1) + mt->raw_to_mcelsius = raw_to_mcelsius_v1; + else if (mt->conf->version == MTK_THERMAL_V2) + mt->raw_to_mcelsius = raw_to_mcelsius_v2; + else + mt->raw_to_mcelsius = raw_to_mcelsius_v3; + + for (ctrl_id = 0; ctrl_id < mt->conf->num_controller ; ctrl_id++) + for (i = 0; i < mt->conf->num_banks; i++) + mtk_thermal_init_bank(mt, i, apmixed_phys_base, + auxadc_phys_base, ctrl_id); + + tzdev = devm_thermal_of_zone_register(&pdev->dev, 0, mt, + &mtk_thermal_ops); + if (IS_ERR(tzdev)) + return PTR_ERR(tzdev); + + ret = devm_thermal_add_hwmon_sysfs(&pdev->dev, tzdev); + if (ret) + dev_warn(&pdev->dev, "error in thermal_add_hwmon_sysfs"); + + return 0; +} + +static struct platform_driver mtk_thermal_driver = { + .probe = mtk_thermal_probe, + .driver = { + .name = "mtk-thermal", + .of_match_table = mtk_thermal_of_match, + }, +}; + +module_platform_driver(mtk_thermal_driver); + +MODULE_AUTHOR("Michael Kao <michael.kao@mediatek.com>"); +MODULE_AUTHOR("Louis Yu <louis.yu@mediatek.com>"); +MODULE_AUTHOR("Dawei Chien <dawei.chien@mediatek.com>"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_AUTHOR("Hanyi Wu <hanyi.wu@mediatek.com>"); +MODULE_DESCRIPTION("Mediatek thermal driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c new file mode 100644 index 0000000000..effd9b00a4 --- /dev/null +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -0,0 +1,1367 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Balsam CHIHI <bchihi@baylibre.com> + */ + +#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.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/thermal.h> +#include <dt-bindings/thermal/mediatek,lvts-thermal.h> + +#include "../thermal_hwmon.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 0 +#define LVTS_GROUP_INTERVAL 0 +#define LVTS_FILTER_INTERVAL 0 +#define LVTS_SENSOR_INTERVAL 0 +#define LVTS_HW_FILTER 0x0 +#define LVTS_TSSEL_CONF 0x13121110 +#define LVTS_CALSCALE_CONF 0x300 +#define LVTS_MONINT_CONF 0x8300318C + +#define LVTS_MONINT_OFFSET_SENSOR0 0xC +#define LVTS_MONINT_OFFSET_SENSOR1 0x180 +#define LVTS_MONINT_OFFSET_SENSOR2 0x3000 +#define LVTS_MONINT_OFFSET_SENSOR3 0x3000000 + +#define LVTS_INT_SENSOR0 0x0009001F +#define LVTS_INT_SENSOR1 0x001203E0 +#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_MSR_READ_TIMEOUT_US 400 +#define LVTS_MSR_READ_WAIT_US (LVTS_MSR_READ_TIMEOUT_US / 2) + +#define LVTS_HW_SHUTDOWN_MT8195 105000 + +#define LVTS_MINIMUM_THRESHOLD 20000 + +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 { + const 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; + int low_thresh; + int high_thresh; +}; + +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; + int low_thresh; + int high_thresh; +}; + +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_DEBUG_FS + struct dentry *dom_dentry; +#endif +}; + +#ifdef CONFIG_MTK_LVTS_THERMAL_DEBUGFS + +#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; + char name[64]; + int i; + + lvts_td->dom_dentry = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR(lvts_td->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, lvts_td->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(struct lvts_domain *lvts_td) +{ + debugfs_remove_recursive(lvts_td->dom_dentry); +} + +#else + +static inline int lvts_debugfs_init(struct device *dev, + struct lvts_domain *lvts_td) +{ + return 0; +} + +static void lvts_debugfs_exit(struct lvts_domain *lvts_td) { } + +#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 = thermal_zone_device_priv(tz); + void __iomem *msr = lvts_sensor->msr; + u32 value; + int rc; + + /* + * Measurement registers: + * + * LVTS_MSR[0-3] / LVTS_IMMD[0-3] + * + * Bits: + * + * 32-17: Unused + * 16 : Valid temperature + * 15-0 : Raw temperature + */ + rc = readl_poll_timeout(msr, value, value & BIT(16), + LVTS_MSR_READ_WAIT_US, LVTS_MSR_READ_TIMEOUT_US); + + /* + * 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 (rc) + return -EAGAIN; + + *temp = lvts_raw_to_temp(value & 0xFFFF); + + return 0; +} + +static void lvts_update_irq_mask(struct lvts_ctrl *lvts_ctrl) +{ + u32 masks[] = { + LVTS_MONINT_OFFSET_SENSOR0, + LVTS_MONINT_OFFSET_SENSOR1, + LVTS_MONINT_OFFSET_SENSOR2, + LVTS_MONINT_OFFSET_SENSOR3, + }; + u32 value = 0; + int i; + + value = readl(LVTS_MONINT(lvts_ctrl->base)); + + for (i = 0; i < ARRAY_SIZE(masks); i++) { + if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh + && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) + value |= masks[i]; + else + value &= ~masks[i]; + } + + writel(value, LVTS_MONINT(lvts_ctrl->base)); +} + +static bool lvts_should_update_thresh(struct lvts_ctrl *lvts_ctrl, int high) +{ + int i; + + if (high > lvts_ctrl->high_thresh) + return true; + + for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) + if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh + && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) + return false; + + return true; +} + +static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct lvts_sensor *lvts_sensor = thermal_zone_device_priv(tz); + struct lvts_ctrl *lvts_ctrl = container_of(lvts_sensor, struct lvts_ctrl, sensors[lvts_sensor->id]); + void __iomem *base = lvts_sensor->base; + u32 raw_low = lvts_temp_to_raw(low != -INT_MAX ? low : LVTS_MINIMUM_THRESHOLD); + u32 raw_high = lvts_temp_to_raw(high); + bool should_update_thresh; + + lvts_sensor->low_thresh = low; + lvts_sensor->high_thresh = high; + + should_update_thresh = lvts_should_update_thresh(lvts_ctrl, high); + if (should_update_thresh) { + lvts_ctrl->high_thresh = high; + lvts_ctrl->low_thresh = low; + } + lvts_update_irq_mask(lvts_ctrl); + + if (!should_update_thresh) + return 0; + + /* + * Low offset temperature threshold + * + * LVTS_OFFSETL + * + * Bits: + * + * 14-0 : Raw temperature for threshold + */ + pr_debug("%s: Setting low limit temperature interrupt: %d\n", + thermal_zone_device_type(tz), low); + writel(raw_low, LVTS_OFFSETL(base)); + + /* + * High offset temperature threshold + * + * LVTS_OFFSETH + * + * Bits: + * + * 14-0 : Raw temperature for threshold + */ + pr_debug("%s: Setting high limit temperature interrupt: %d\n", + thermal_zone_device_type(tz), high); + writel(raw_high, LVTS_OFFSETH(base)); + + return 0; +} + +static irqreturn_t lvts_ctrl_irq_handler(struct lvts_ctrl *lvts_ctrl) +{ + irqreturn_t iret = IRQ_NONE; + u32 value; + u32 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 0010 0000 0011 1110 0000 + * => 0X001203E0 + * 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[i]); + 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 lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, + const 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_sensor[i].low_thresh = INT_MIN; + lvts_sensor[i].high_thresh = INT_MIN; + }; + + 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: + * + * stream index map for MCU Domain : + * + * <-----mcu-tc#0-----> <-----sensor#0-----> <-----sensor#1-----> + * 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x09 + * + * <-----mcu-tc#1-----> <-----sensor#2-----> <-----sensor#3-----> + * 0x0A | 0x0B | 0x0C | 0x0D | 0x0E | 0x0F | 0x10 | 0x11 | 0x12 + * + * <-----mcu-tc#2-----> <-----sensor#4-----> <-----sensor#5-----> <-----sensor#6-----> <-----sensor#7-----> + * 0x13 | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 | 0x1A | 0x1B | 0x1C | 0x1D | 0x1E | 0x1F | 0x20 | 0x21 + * + * stream index map for AP Domain : + * + * <-----ap--tc#0-----> <-----sensor#0-----> <-----sensor#1-----> + * 0x22 | 0x23 | 0x24 | 0x25 | 0x26 | 0x27 | 0x28 | 0x29 | 0x2A + * + * <-----ap--tc#1-----> <-----sensor#2-----> <-----sensor#3-----> + * 0x2B | 0x2C | 0x2D | 0x2E | 0x2F | 0x30 | 0x31 | 0x32 | 0x33 + * + * <-----ap--tc#2-----> <-----sensor#4-----> <-----sensor#5-----> <-----sensor#6-----> + * 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39 | 0x3A | 0x3B | 0x3C | 0x3D | 0x3E | 0x3F + * + * <-----ap--tc#3-----> <-----sensor#7-----> <-----sensor#8-----> + * 0x40 | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 + * + * The data description gives the offset of the calibration data in + * this bytes stream for each sensor. + */ +static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, + const 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, + const 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_err(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_err(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 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 lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, + const 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); + + lvts_ctrl[i].low_thresh = INT_MIN; + lvts_ctrl[i].high_thresh = INT_MIN; + } + + /* + * 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 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_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; + /* + * Bitmaps to enable each sensor on immediate and filtered modes, as + * described in MSRCTL1 and MONCTL0 registers below, respectively. + */ + u32 sensor_imm_bitmap[] = { BIT(4), BIT(5), BIT(6), BIT(9) }; + u32 sensor_filt_bitmap[] = { BIT(0), BIT(1), BIT(2), BIT(3) }; + + u32 *sensor_bitmap = lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE ? + sensor_imm_bitmap : sensor_filt_bitmap; + + 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); + } + + devm_thermal_add_hwmon_sysfs(dev, 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 |= sensor_bitmap[i]; + } + + /* + * 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. + */ + if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) { + /* + * 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 in MONCTL1 and MONCTL2 + */ + writel(sensor_map, LVTS_MSRCTL1(lvts_ctrl->base)); + } else { + /* + * Bits: + * 9: Single point access flow + * 0-3: Enable sensing point 0-3 + */ + 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, + const 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_set_enable(lvts_ctrl, true); + 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) +{ + const 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 = of_device_get_match_data(dev); + + lvts_td->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(lvts_td->clk)) + return dev_err_probe(dev, PTR_ERR(lvts_td->clk), "Failed to retrieve clock\n"); + + res = platform_get_mem_or_io(pdev, 0); + if (!res) + return dev_err_probe(dev, (-ENXIO), "No IO resource\n"); + + lvts_td->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(lvts_td->base)) + return dev_err_probe(dev, PTR_ERR(lvts_td->base), "Failed to map io resource\n"); + + lvts_td->reset = devm_reset_control_get_by_index(dev, 0); + if (IS_ERR(lvts_td->reset)) + return dev_err_probe(dev, PTR_ERR(lvts_td->reset), "Failed to get reset control\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = lvts_domain_init(dev, lvts_td, lvts_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize the lvts domain\n"); + + /* + * 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) + return dev_err_probe(dev, ret, "Failed to request interrupt\n"); + + platform_set_drvdata(pdev, lvts_td); + + return 0; +} + +static int lvts_remove(struct platform_device *pdev) +{ + struct lvts_domain *lvts_td; + int i; + + lvts_td = platform_get_drvdata(pdev); + + for (i = 0; i < lvts_td->num_lvts_ctrl; i++) + lvts_ctrl_set_enable(&lvts_td->lvts_ctrl[i], false); + + lvts_debugfs_exit(lvts_td); + + return 0; +} + +static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { + { + .cal_offset = { 0x04, 0x07 }, + .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 = { 0x0d, 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 const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { + { + .cal_offset = { 0x25, 0x28 }, + .lvts_sensor = { + { .dt_id = MT8195_AP_VPU0 }, + { .dt_id = MT8195_AP_VPU1 } + }, + .num_lvts_sensor = 2, + .offset = 0x0, + .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, + }, + { + .cal_offset = { 0x2e, 0x31 }, + .lvts_sensor = { + { .dt_id = MT8195_AP_GPU0 }, + { .dt_id = MT8195_AP_GPU1 } + }, + .num_lvts_sensor = 2, + .offset = 0x100, + .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, + }, + { + .cal_offset = { 0x37, 0x3a, 0x3d }, + .lvts_sensor = { + { .dt_id = MT8195_AP_VDEC }, + { .dt_id = MT8195_AP_IMG }, + { .dt_id = MT8195_AP_INFRA }, + }, + .num_lvts_sensor = 3, + .offset = 0x200, + .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, + }, + { + .cal_offset = { 0x43, 0x46 }, + .lvts_sensor = { + { .dt_id = MT8195_AP_CAM0 }, + { .dt_id = MT8195_AP_CAM1 } + }, + .num_lvts_sensor = 2, + .offset = 0x300, + .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, + } +}; + +static const struct lvts_data mt8195_lvts_mcu_data = { + .lvts_ctrl = mt8195_lvts_mcu_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_mcu_data_ctrl), +}; + +static const struct lvts_data mt8195_lvts_ap_data = { + .lvts_ctrl = mt8195_lvts_ap_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_ap_data_ctrl), +}; + +static const struct of_device_id lvts_of_match[] = { + { .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data }, + { .compatible = "mediatek,mt8195-lvts-ap", .data = &mt8195_lvts_ap_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 <bchihi@baylibre.com>"); +MODULE_DESCRIPTION("MediaTek LVTS Thermal Driver"); +MODULE_LICENSE("GPL"); |