summaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/Kconfig21
-rw-r--r--drivers/thermal/Makefile2
-rw-r--r--drivers/thermal/airoha_thermal.c489
-rw-r--r--drivers/thermal/amlogic_thermal.c16
-rw-r--r--drivers/thermal/armada_thermal.c2
-rw-r--r--drivers/thermal/broadcom/bcm2835_thermal.c10
-rw-r--r--drivers/thermal/broadcom/brcmstb_thermal.c19
-rw-r--r--drivers/thermal/cpufreq_cooling.c7
-rw-r--r--drivers/thermal/cpuidle_cooling.c2
-rw-r--r--drivers/thermal/da9062-thermal.c2
-rw-r--r--drivers/thermal/devfreq_cooling.c5
-rw-r--r--drivers/thermal/dove_thermal.c2
-rw-r--r--drivers/thermal/gov_power_allocator.c7
-rw-r--r--drivers/thermal/gov_step_wise.c25
-rw-r--r--drivers/thermal/hisi_thermal.c4
-rw-r--r--drivers/thermal/imx91_thermal.c388
-rw-r--r--drivers/thermal/imx_thermal.c2
-rw-r--r--drivers/thermal/intel/Kconfig3
-rw-r--r--drivers/thermal/intel/int340x_thermal/Kconfig1
-rw-r--r--drivers/thermal/intel/int340x_thermal/Makefile2
-rw-r--r--drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c9
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c26
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3402_thermal.c3
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3403_thermal.c2
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c8
-rw-r--r--drivers/thermal/intel/int340x_thermal/platform_temperature_control.c313
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.c43
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.h12
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c26
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c83
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c88
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c290
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c57
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c7
-rw-r--r--drivers/thermal/intel/intel_hfi.c33
-rw-r--r--drivers/thermal/intel/intel_pch_thermal.c1
-rw-r--r--drivers/thermal/intel/intel_powerclamp.c7
-rw-r--r--drivers/thermal/intel/intel_quark_dts_thermal.c2
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.c2
-rw-r--r--drivers/thermal/intel/intel_tcc.c10
-rw-r--r--drivers/thermal/intel/intel_tcc_cooling.c18
-rw-r--r--drivers/thermal/intel/therm_throt.c15
-rw-r--r--drivers/thermal/intel/x86_pkg_temp_thermal.c10
-rw-r--r--drivers/thermal/k3_j72xx_bandgap.c8
-rw-r--r--drivers/thermal/kirkwood_thermal.c2
-rw-r--r--drivers/thermal/loongson2_thermal.c15
-rw-r--r--drivers/thermal/mediatek/lvts_thermal.c559
-rw-r--r--drivers/thermal/qcom/Kconfig3
-rw-r--r--drivers/thermal/qcom/lmh.c8
-rw-r--r--drivers/thermal/qcom/qcom-spmi-temp-alarm.c597
-rw-r--r--drivers/thermal/qcom/tsens-v1.c62
-rw-r--r--drivers/thermal/qcom/tsens-v2.c178
-rw-r--r--drivers/thermal/qcom/tsens.c31
-rw-r--r--drivers/thermal/qcom/tsens.h7
-rw-r--r--drivers/thermal/qoriq_thermal.c47
-rw-r--r--drivers/thermal/renesas/Kconfig21
-rw-r--r--drivers/thermal/renesas/Makefile2
-rw-r--r--drivers/thermal/renesas/rcar_gen3_thermal.c180
-rw-r--r--drivers/thermal/renesas/rcar_thermal.c10
-rw-r--r--drivers/thermal/renesas/rzg3e_thermal.c571
-rw-r--r--drivers/thermal/renesas/rzg3s_thermal.c272
-rw-r--r--drivers/thermal/rockchip_thermal.c302
-rw-r--r--drivers/thermal/spear_thermal.c4
-rw-r--r--drivers/thermal/sprd_thermal.c6
-rw-r--r--drivers/thermal/st/st_thermal.c2
-rw-r--r--drivers/thermal/st/stm_thermal.c4
-rw-r--r--drivers/thermal/tegra/Makefile1
-rw-r--r--drivers/thermal/tegra/soctherm-fuse.c18
-rw-r--r--drivers/thermal/tegra/soctherm.c26
-rw-r--r--drivers/thermal/tegra/soctherm.h11
-rw-r--r--drivers/thermal/tegra/tegra114-soctherm.c209
-rw-r--r--drivers/thermal/tegra/tegra124-soctherm.c4
-rw-r--r--drivers/thermal/tegra/tegra132-soctherm.c4
-rw-r--r--drivers/thermal/tegra/tegra210-soctherm.c4
-rw-r--r--drivers/thermal/testing/command.c30
-rw-r--r--drivers/thermal/testing/zone.c32
-rw-r--r--drivers/thermal/thermal-generic-adc.c55
-rw-r--r--drivers/thermal/thermal_core.c190
-rw-r--r--drivers/thermal/thermal_core.h8
-rw-r--r--drivers/thermal/thermal_debugfs.c12
-rw-r--r--drivers/thermal/thermal_hwmon.c10
-rw-r--r--drivers/thermal/thermal_of.c33
-rw-r--r--drivers/thermal/thermal_sysfs.c52
-rw-r--r--drivers/thermal/thermal_thresholds.c2
84 files changed, 4950 insertions, 716 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index d3f9686e26e7..b10080d61860 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -257,7 +257,7 @@ config HISI_THERMAL
depends on ARCH_HISI || COMPILE_TEST
depends on HAS_IOMEM
depends on OF
- default y
+ default ARCH_HISI
help
Enable this to plug hisilicon's thermal sensor driver into the Linux
thermal framework. cpufreq is used as the cooling device to throttle
@@ -296,6 +296,16 @@ config IMX8MM_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the passive
trip is crossed.
+config IMX91_THERMAL
+ tristate "Temperature sensor driver for NXP i.MX91 SoC"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on OF
+ help
+ Include one sensor and six comparators. Each of them compares the
+ temperature value (from the sensor) against the programmable
+ threshold values. The direction of the comparison is configurable
+ (greater / lesser than).
+
config K3_THERMAL
tristate "Texas Instruments K3 thermal support"
depends on ARCH_K3 || COMPILE_TEST
@@ -327,6 +337,15 @@ config QORIQ_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
+config AIROHA_THERMAL
+ tristate "Airoha thermal sensor driver"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ depends on MFD_SYSCON
+ depends on OF
+ help
+ Enable this to plug the Airoha thermal sensor driver into the Linux
+ thermal framework.
+
config SPEAR_THERMAL
tristate "SPEAr thermal sensor driver"
depends on PLAT_SPEAR || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 9abf43a74f2b..bb21e7ea7fc6 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_K3_THERMAL) += k3_bandgap.o k3_j72xx_bandgap.o
# platform thermal drivers
obj-y += broadcom/
obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
+obj-$(CONFIG_AIROHA_THERMAL) += airoha_thermal.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
@@ -50,6 +51,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
+obj-$(CONFIG_IMX91_THERMAL) += imx91_thermal.o
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
diff --git a/drivers/thermal/airoha_thermal.c b/drivers/thermal/airoha_thermal.c
new file mode 100644
index 000000000000..b9fd6bfc88e5
--- /dev/null
+++ b/drivers/thermal/airoha_thermal.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+
+/* SCU regs */
+#define EN7581_PLLRG_PROTECT 0x268
+#define EN7581_PWD_TADC 0x2ec
+#define EN7581_MUX_TADC GENMASK(3, 1)
+#define EN7581_DOUT_TADC 0x2f8
+#define EN7581_DOUT_TADC_MASK GENMASK(15, 0)
+
+/* PTP_THERMAL regs */
+#define EN7581_TEMPMONCTL0 0x800
+#define EN7581_SENSE3_EN BIT(3)
+#define EN7581_SENSE2_EN BIT(2)
+#define EN7581_SENSE1_EN BIT(1)
+#define EN7581_SENSE0_EN BIT(0)
+#define EN7581_TEMPMONCTL1 0x804
+/* period unit calculated in BUS clock * 256 scaling-up */
+#define EN7581_PERIOD_UNIT GENMASK(9, 0)
+#define EN7581_TEMPMONCTL2 0x808
+#define EN7581_FILT_INTERVAL GENMASK(25, 16)
+#define EN7581_SEN_INTERVAL GENMASK(9, 0)
+#define EN7581_TEMPMONINT 0x80C
+#define EN7581_STAGE3_INT_EN BIT(31)
+#define EN7581_STAGE2_INT_EN BIT(30)
+#define EN7581_STAGE1_INT_EN BIT(29)
+#define EN7581_FILTER_INT_EN_3 BIT(28)
+#define EN7581_IMMD_INT_EN3 BIT(27)
+#define EN7581_NOHOTINTEN3 BIT(26)
+#define EN7581_HOFSINTEN3 BIT(25)
+#define EN7581_LOFSINTEN3 BIT(24)
+#define EN7581_HINTEN3 BIT(23)
+#define EN7581_CINTEN3 BIT(22)
+#define EN7581_FILTER_INT_EN_2 BIT(21)
+#define EN7581_FILTER_INT_EN_1 BIT(20)
+#define EN7581_FILTER_INT_EN_0 BIT(19)
+#define EN7581_IMMD_INT_EN2 BIT(18)
+#define EN7581_IMMD_INT_EN1 BIT(17)
+#define EN7581_IMMD_INT_EN0 BIT(16)
+#define EN7581_TIME_OUT_INT_EN BIT(15)
+#define EN7581_NOHOTINTEN2 BIT(14)
+#define EN7581_HOFSINTEN2 BIT(13)
+#define EN7581_LOFSINTEN2 BIT(12)
+#define EN7581_HINTEN2 BIT(11)
+#define EN7581_CINTEN2 BIT(10)
+#define EN7581_NOHOTINTEN1 BIT(9)
+#define EN7581_HOFSINTEN1 BIT(8)
+#define EN7581_LOFSINTEN1 BIT(7)
+#define EN7581_HINTEN1 BIT(6)
+#define EN7581_CINTEN1 BIT(5)
+#define EN7581_NOHOTINTEN0 BIT(4)
+/* Similar to COLD and HOT also these seems to be swapped in documentation */
+#define EN7581_LOFSINTEN0 BIT(3) /* In documentation: BIT(2) */
+#define EN7581_HOFSINTEN0 BIT(2) /* In documentation: BIT(3) */
+/* It seems documentation have these swapped as the HW
+ * - Fire BIT(1) when lower than EN7581_COLD_THRE
+ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or
+ * EN7581_HOT_THRE
+ */
+#define EN7581_CINTEN0 BIT(1) /* In documentation: BIT(0) */
+#define EN7581_HINTEN0 BIT(0) /* In documentation: BIT(1) */
+#define EN7581_TEMPMONINTSTS 0x810
+#define EN7581_STAGE3_INT_STAT BIT(31)
+#define EN7581_STAGE2_INT_STAT BIT(30)
+#define EN7581_STAGE1_INT_STAT BIT(29)
+#define EN7581_FILTER_INT_STAT_3 BIT(28)
+#define EN7581_IMMD_INT_STS3 BIT(27)
+#define EN7581_NOHOTINTSTS3 BIT(26)
+#define EN7581_HOFSINTSTS3 BIT(25)
+#define EN7581_LOFSINTSTS3 BIT(24)
+#define EN7581_HINTSTS3 BIT(23)
+#define EN7581_CINTSTS3 BIT(22)
+#define EN7581_FILTER_INT_STAT_2 BIT(21)
+#define EN7581_FILTER_INT_STAT_1 BIT(20)
+#define EN7581_FILTER_INT_STAT_0 BIT(19)
+#define EN7581_IMMD_INT_STS2 BIT(18)
+#define EN7581_IMMD_INT_STS1 BIT(17)
+#define EN7581_IMMD_INT_STS0 BIT(16)
+#define EN7581_TIME_OUT_INT_STAT BIT(15)
+#define EN7581_NOHOTINTSTS2 BIT(14)
+#define EN7581_HOFSINTSTS2 BIT(13)
+#define EN7581_LOFSINTSTS2 BIT(12)
+#define EN7581_HINTSTS2 BIT(11)
+#define EN7581_CINTSTS2 BIT(10)
+#define EN7581_NOHOTINTSTS1 BIT(9)
+#define EN7581_HOFSINTSTS1 BIT(8)
+#define EN7581_LOFSINTSTS1 BIT(7)
+#define EN7581_HINTSTS1 BIT(6)
+#define EN7581_CINTSTS1 BIT(5)
+#define EN7581_NOHOTINTSTS0 BIT(4)
+/* Similar to COLD and HOT also these seems to be swapped in documentation */
+#define EN7581_LOFSINTSTS0 BIT(3) /* In documentation: BIT(2) */
+#define EN7581_HOFSINTSTS0 BIT(2) /* In documentation: BIT(3) */
+/* It seems documentation have these swapped as the HW
+ * - Fire BIT(1) when lower than EN7581_COLD_THRE
+ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or
+ * EN7581_HOT_THRE
+ *
+ * To clear things, we swap the define but we keep them documented here.
+ */
+#define EN7581_CINTSTS0 BIT(1) /* In documentation: BIT(0) */
+#define EN7581_HINTSTS0 BIT(0) /* In documentation: BIT(1)*/
+/* Monitor will take the bigger threshold between HOT2NORMAL and HOT
+ * and will fire both HOT2NORMAL and HOT interrupt when higher than the 2
+ *
+ * It has also been observed that not setting HOT2NORMAL makes the monitor
+ * treat COLD threshold as HOT2NORMAL.
+ */
+#define EN7581_TEMPH2NTHRE 0x824
+/* It seems HOT2NORMAL is actually NORMAL2HOT */
+#define EN7581_HOT2NORMAL_THRE GENMASK(11, 0)
+#define EN7581_TEMPHTHRE 0x828
+#define EN7581_HOT_THRE GENMASK(11, 0)
+/* Monitor will use this as HOT2NORMAL (fire interrupt when lower than...)*/
+#define EN7581_TEMPCTHRE 0x82c
+#define EN7581_COLD_THRE GENMASK(11, 0)
+/* Also LOW and HIGH offset register are swapped */
+#define EN7581_TEMPOFFSETL 0x830 /* In documentation: 0x834 */
+#define EN7581_LOW_OFFSET GENMASK(11, 0)
+#define EN7581_TEMPOFFSETH 0x834 /* In documentation: 0x830 */
+#define EN7581_HIGH_OFFSET GENMASK(11, 0)
+#define EN7581_TEMPMSRCTL0 0x838
+#define EN7581_MSRCTL3 GENMASK(11, 9)
+#define EN7581_MSRCTL2 GENMASK(8, 6)
+#define EN7581_MSRCTL1 GENMASK(5, 3)
+#define EN7581_MSRCTL0 GENMASK(2, 0)
+#define EN7581_TEMPADCVALIDADDR 0x878
+#define EN7581_ADC_VALID_ADDR GENMASK(31, 0)
+#define EN7581_TEMPADCVOLTADDR 0x87c
+#define EN7581_ADC_VOLT_ADDR GENMASK(31, 0)
+#define EN7581_TEMPRDCTRL 0x880
+/*
+ * NOTICE: AHB have this set to 0 by default. Means that
+ * the same addr is used for ADC volt and valid reading.
+ * In such case, VALID ADDR is used and volt addr is ignored.
+ */
+#define EN7581_RD_CTRL_DIFF BIT(0)
+#define EN7581_TEMPADCVALIDMASK 0x884
+#define EN7581_ADV_RD_VALID_POLARITY BIT(5)
+#define EN7581_ADV_RD_VALID_POS GENMASK(4, 0)
+#define EN7581_TEMPADCVOLTAGESHIFT 0x888
+#define EN7581_ADC_VOLTAGE_SHIFT GENMASK(4, 0)
+/*
+ * Same values for each CTL.
+ * Can operate in:
+ * - 1 sample
+ * - 2 sample and make average of them
+ * - 4,6,10,16 sample, drop max and min and make average of them
+ */
+#define EN7581_MSRCTL_1SAMPLE 0x0
+#define EN7581_MSRCTL_AVG2SAMPLE 0x1
+#define EN7581_MSRCTL_4SAMPLE_MAX_MIX_AVG2 0x2
+#define EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4 0x3
+#define EN7581_MSRCTL_10SAMPLE_MAX_MIX_AVG8 0x4
+#define EN7581_MSRCTL_18SAMPLE_MAX_MIX_AVG16 0x5
+#define EN7581_TEMPAHBPOLL 0x840
+#define EN7581_ADC_POLL_INTVL GENMASK(31, 0)
+/* PTPSPARE0,2 reg are used to store efuse info for calibrated temp offset */
+#define EN7581_EFUSE_TEMP_OFFSET_REG 0xf20 /* PTPSPARE0 */
+#define EN7581_EFUSE_TEMP_OFFSET GENMASK(31, 16)
+#define EN7581_PTPSPARE1 0xf24 /* PTPSPARE1 */
+#define EN7581_EFUSE_TEMP_CPU_SENSOR_REG 0xf28 /* PTPSPARE2 */
+
+#define EN7581_SLOPE_X100_DIO_DEFAULT 5645
+#define EN7581_SLOPE_X100_DIO_AVS 5645
+
+#define EN7581_INIT_TEMP_CPK_X10 300
+#define EN7581_INIT_TEMP_FTK_X10 620
+#define EN7581_INIT_TEMP_NONK_X10 550
+
+#define EN7581_SCU_THERMAL_PROTECT_KEY 0x12
+#define EN7581_SCU_THERMAL_MUX_DIODE1 0x7
+
+/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */
+#define TEMP_TO_RAW(priv, temp) ((((((temp) / 100) - (priv)->init_temp) * \
+ (priv)->default_slope) / 1000) + \
+ (priv)->default_offset)
+
+/* Convert raw to temp ((((temp - offset) * 1000) / slope + init) * 100) */
+#define RAW_TO_TEMP(priv, raw) (((((raw) - (priv)->default_offset) * 1000) / \
+ (priv)->default_slope + \
+ (priv)->init_temp) * 100)
+
+#define AIROHA_MAX_SAMPLES 6
+
+struct airoha_thermal_priv {
+ void __iomem *base;
+ struct regmap *chip_scu;
+ struct resource scu_adc_res;
+
+ struct thermal_zone_device *tz;
+ int init_temp;
+ int default_slope;
+ int default_offset;
+};
+
+static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv)
+{
+ u32 val;
+
+ regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val);
+ return FIELD_GET(EN7581_DOUT_TADC_MASK, val);
+}
+
+static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv)
+{
+ u32 adc_mux, pllrg;
+
+ /* Save PLLRG current value */
+ regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg);
+
+ /* Give access to thermal regs */
+ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY);
+ adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1);
+ regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux);
+
+ /* Restore PLLRG value on exit */
+ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
+}
+
+static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
+ int min_value, max_value, avg_value, value;
+ int i;
+
+ avg_value = 0;
+ min_value = INT_MAX;
+ max_value = INT_MIN;
+
+ for (i = 0; i < AIROHA_MAX_SAMPLES; i++) {
+ value = airoha_get_thermal_ADC(priv);
+ min_value = min(value, min_value);
+ max_value = max(value, max_value);
+ avg_value += value;
+ }
+
+ /* Drop min and max and average for the remaining sample */
+ avg_value -= (min_value + max_value);
+ avg_value /= AIROHA_MAX_SAMPLES - 2;
+
+ *temp = RAW_TO_TEMP(priv, avg_value);
+ return 0;
+}
+
+static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low,
+ int high)
+{
+ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
+ bool enable_monitor = false;
+
+ if (high != INT_MAX) {
+ /* Validate high and clamp it a supported value */
+ high = clamp_t(int, high, RAW_TO_TEMP(priv, 0),
+ RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK)));
+
+ /* We offset the high temp of 1°C to trigger correct event */
+ writel(TEMP_TO_RAW(priv, high) >> 4,
+ priv->base + EN7581_TEMPOFFSETH);
+
+ enable_monitor = true;
+ }
+
+ if (low != -INT_MAX) {
+ /* Validate low and clamp it to a supported value */
+ low = clamp_t(int, high, RAW_TO_TEMP(priv, 0),
+ RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK)));
+
+ /* We offset the low temp of 1°C to trigger correct event */
+ writel(TEMP_TO_RAW(priv, low) >> 4,
+ priv->base + EN7581_TEMPOFFSETL);
+
+ enable_monitor = true;
+ }
+
+ /* Enable sensor 0 monitor after trip are set */
+ if (enable_monitor)
+ writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0);
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops thdev_ops = {
+ .get_temp = airoha_thermal_get_temp,
+ .set_trips = airoha_thermal_set_trips,
+};
+
+static irqreturn_t airoha_thermal_irq(int irq, void *data)
+{
+ struct airoha_thermal_priv *priv = data;
+ enum thermal_notify_event event;
+ bool update = false;
+ u32 status;
+
+ status = readl(priv->base + EN7581_TEMPMONINTSTS);
+ switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) {
+ case EN7581_HOFSINTSTS0:
+ event = THERMAL_TRIP_VIOLATED;
+ update = true;
+ break;
+ case EN7581_LOFSINTSTS0:
+ event = THERMAL_EVENT_UNSPECIFIED;
+ update = true;
+ break;
+ default:
+ /* Should be impossible as we enable only these Interrupt */
+ break;
+ }
+
+ /* Reset Interrupt */
+ writel(status, priv->base + EN7581_TEMPMONINTSTS);
+
+ if (update)
+ thermal_zone_device_update(priv->tz, event);
+
+ return IRQ_HANDLED;
+}
+
+static void airoha_thermal_setup_adc_val(struct device *dev,
+ struct airoha_thermal_priv *priv)
+{
+ u32 efuse_calib_info, cpu_sensor;
+
+ /* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */
+ airoha_init_thermal_ADC_mode(priv);
+ /* sleep 10 ms for ADC to enable */
+ usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC);
+
+ efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG);
+ if (efuse_calib_info) {
+ priv->default_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info);
+ /* Different slope are applied if the sensor is used for CPU or for package */
+ cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG);
+ if (cpu_sensor) {
+ priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT;
+ priv->init_temp = EN7581_INIT_TEMP_FTK_X10;
+ } else {
+ priv->default_slope = EN7581_SLOPE_X100_DIO_AVS;
+ priv->init_temp = EN7581_INIT_TEMP_CPK_X10;
+ }
+ } else {
+ priv->default_offset = airoha_get_thermal_ADC(priv);
+ priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT;
+ priv->init_temp = EN7581_INIT_TEMP_NONK_X10;
+ dev_info(dev, "missing thermal calibration EFUSE, using non calibrated value\n");
+ }
+}
+
+static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv)
+{
+ /* Set measure mode */
+ writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4),
+ priv->base + EN7581_TEMPMSRCTL0);
+
+ /*
+ * Configure ADC valid reading addr
+ * The AHB temp monitor system doesn't have direct access to the
+ * thermal sensor. It does instead work by providing various
+ * addresses to configure how to access and setup an ADC for the
+ * sensor. EN7581 supports only one sensor hence the
+ * implementation is greatly simplified but the AHB supports
+ * up to 4 different sensors from the same ADC that can be
+ * switched by tuning the ADC mux or writing address.
+ *
+ * We set valid instead of volt as we don't enable valid/volt
+ * split reading and AHB read valid addr in such case.
+ */
+ writel(priv->scu_adc_res.start + EN7581_DOUT_TADC,
+ priv->base + EN7581_TEMPADCVALIDADDR);
+
+ /*
+ * Configure valid bit on a fake value of bit 16. The ADC outputs
+ * max of 2 bytes for voltage.
+ */
+ writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16),
+ priv->base + EN7581_TEMPADCVALIDMASK);
+
+ /*
+ * AHB supports max 12 bytes for ADC voltage. Shift the read
+ * value 4 bit to the right. Precision lost by this is minimal
+ * in the order of half a °C and is acceptable in the context
+ * of triggering interrupt in critical condition.
+ */
+ writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4),
+ priv->base + EN7581_TEMPADCVOLTAGESHIFT);
+
+ /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */
+ writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3),
+ priv->base + EN7581_TEMPMONCTL1);
+
+ /*
+ * filt interval is 1 * 52.715us = 52.715us,
+ * sen interval is 379 * 52.715us = 19.97ms
+ */
+ writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) |
+ FIELD_PREP(EN7581_FILT_INTERVAL, 379),
+ priv->base + EN7581_TEMPMONCTL2);
+
+ /* AHB poll is set to 146 * 68.64 = 10.02us */
+ writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146),
+ priv->base + EN7581_TEMPAHBPOLL);
+}
+
+static int airoha_thermal_probe(struct platform_device *pdev)
+{
+ struct airoha_thermal_priv *priv;
+ struct device_node *chip_scu_np;
+ struct device *dev = &pdev->dev;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0);
+ if (!chip_scu_np)
+ return -EINVAL;
+
+ priv->chip_scu = syscon_node_to_regmap(chip_scu_np);
+ if (IS_ERR(priv->chip_scu))
+ return PTR_ERR(priv->chip_scu);
+
+ of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
+ of_node_put(chip_scu_np);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ airoha_thermal_irq, IRQF_ONESHOT,
+ pdev->name, priv);
+ if (ret) {
+ dev_err(dev, "Can't get interrupt working.\n");
+ return ret;
+ }
+
+ airoha_thermal_setup_monitor(priv);
+ airoha_thermal_setup_adc_val(dev, priv);
+
+ /* register of thermal sensor and get info from DT */
+ priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &thdev_ops);
+ if (IS_ERR(priv->tz)) {
+ dev_err(dev, "register thermal zone sensor failed\n");
+ return PTR_ERR(priv->tz);
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ /* Enable LOW and HIGH interrupt */
+ writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0,
+ priv->base + EN7581_TEMPMONINT);
+
+ return 0;
+}
+
+static const struct of_device_id airoha_thermal_match[] = {
+ { .compatible = "airoha,en7581-thermal" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, airoha_thermal_match);
+
+static struct platform_driver airoha_thermal_driver = {
+ .driver = {
+ .name = "airoha-thermal",
+ .of_match_table = airoha_thermal_match,
+ },
+ .probe = airoha_thermal_probe,
+};
+
+module_platform_driver(airoha_thermal_driver);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Airoha thermal driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c
index 3c5f7dbddf2c..5448d772db12 100644
--- a/drivers/thermal/amlogic_thermal.c
+++ b/drivers/thermal/amlogic_thermal.c
@@ -7,10 +7,10 @@
*
* Register value to celsius temperature formulas:
* Read_Val m * U
- * U = ---------, Uptat = ---------
+ * U = ---------, uptat = ---------
* 2^16 1 + n * U
*
- * Temperature = A * ( Uptat + u_efuse / 2^16 )- B
+ * Temperature = A * ( uptat + u_efuse / 2^16 )- B
*
* A B m n : calibration parameters
* u_efuse : fused calibration value, it's a signed 16 bits value
@@ -112,7 +112,7 @@ static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata,
const struct amlogic_thermal_soc_calib_data *param =
pdata->data->calibration_parameters;
int temp;
- s64 factor, Uptat, uefuse;
+ s64 factor, uptat, uefuse;
uefuse = pdata->trim_info & TSENSOR_TRIM_SIGN_MASK ?
~(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK) + 1 :
@@ -121,12 +121,12 @@ static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata,
factor = param->n * temp_code;
factor = div_s64(factor, 100);
- Uptat = temp_code * param->m;
- Uptat = div_s64(Uptat, 100);
- Uptat = Uptat * BIT(16);
- Uptat = div_s64(Uptat, BIT(16) + factor);
+ uptat = temp_code * param->m;
+ uptat = div_s64(uptat, 100);
+ uptat = uptat * BIT(16);
+ uptat = div_s64(uptat, BIT(16) + factor);
- temp = (Uptat + uefuse) * param->A;
+ temp = (uptat + uefuse) * param->A;
temp = div_s64(temp, BIT(16));
temp = (temp - param->B) * 100;
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
index 9bff21068721..c2fbdb534f61 100644
--- a/drivers/thermal/armada_thermal.c
+++ b/drivers/thermal/armada_thermal.c
@@ -408,7 +408,7 @@ static int armada_get_temp_legacy(struct thermal_zone_device *thermal,
return ret;
}
-static struct thermal_zone_device_ops legacy_ops = {
+static const struct thermal_zone_device_ops legacy_ops = {
.get_temp = armada_get_temp_legacy,
};
diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c
index 7fbba2233c4c..c5105dfc6ec9 100644
--- a/drivers/thermal/broadcom/bcm2835_thermal.c
+++ b/drivers/thermal/broadcom/bcm2835_thermal.c
@@ -11,6 +11,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -80,12 +81,7 @@ static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)
temp -= offset;
temp /= slope;
- if (temp < 0)
- temp = 0;
- if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
- temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
-
- return temp;
+ return clamp(temp, 0, (int)BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1);
}
static int bcm2835_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
@@ -192,7 +188,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
rate = clk_get_rate(data->clk);
if ((rate < 1920000) || (rate > 5000000))
dev_warn(dev,
- "Clock %pCn running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n",
+ "Clock %pC running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n",
data->clk, rate);
/* register of thermal sensor and get info from DT */
diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c
index 270982740fde..a9ffa596f7c0 100644
--- a/drivers/thermal/broadcom/brcmstb_thermal.c
+++ b/drivers/thermal/broadcom/brcmstb_thermal.c
@@ -16,6 +16,7 @@
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -154,7 +155,7 @@ static int brcmstb_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct brcmstb_thermal_priv *priv = thermal_zone_device_priv(tz);
u32 val;
- long t;
+ int t;
val = __raw_readl(priv->tmon_base + AVS_TMON_STATUS);
@@ -164,10 +165,7 @@ static int brcmstb_get_temp(struct thermal_zone_device *tz, int *temp)
val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift;
t = avs_tmon_code_to_temp(priv, val);
- if (t < 0)
- *temp = 0;
- else
- *temp = t;
+ *temp = max(0, t);
return 0;
}
@@ -286,14 +284,20 @@ static int brcmstb_set_trips(struct thermal_zone_device *tz, int low, int high)
return 0;
}
-static const struct thermal_zone_device_ops brcmstb_16nm_of_ops = {
+static const struct thermal_zone_device_ops brcmstb_of_ops = {
.get_temp = brcmstb_get_temp,
};
+static const struct brcmstb_thermal_params brcmstb_8nm_params = {
+ .offset = 418670,
+ .mult = 509,
+ .of_ops = &brcmstb_of_ops,
+};
+
static const struct brcmstb_thermal_params brcmstb_16nm_params = {
.offset = 457829,
.mult = 557,
- .of_ops = &brcmstb_16nm_of_ops,
+ .of_ops = &brcmstb_of_ops,
};
static const struct thermal_zone_device_ops brcmstb_28nm_of_ops = {
@@ -308,6 +312,7 @@ static const struct brcmstb_thermal_params brcmstb_28nm_params = {
};
static const struct of_device_id brcmstb_thermal_id_table[] = {
+ { .compatible = "brcm,avs-tmon-bcm74110", .data = &brcmstb_8nm_params },
{ .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params },
{ .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params },
{},
diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c
index 6b7ab1814c12..32bf5ab44f4a 100644
--- a/drivers/thermal/cpufreq_cooling.c
+++ b/drivers/thermal/cpufreq_cooling.c
@@ -371,9 +371,8 @@ static int allocate_idle_time(struct cpufreq_cooling_device *cpufreq_cdev)
{
unsigned int num_cpus = cpumask_weight(cpufreq_cdev->policy->related_cpus);
- cpufreq_cdev->idle_time = kcalloc(num_cpus,
- sizeof(*cpufreq_cdev->idle_time),
- GFP_KERNEL);
+ cpufreq_cdev->idle_time = kzalloc_objs(*cpufreq_cdev->idle_time,
+ num_cpus);
if (!cpufreq_cdev->idle_time)
return -ENOMEM;
@@ -543,7 +542,7 @@ __cpufreq_cooling_register(struct device_node *np,
return ERR_PTR(-ENODEV);
}
- cpufreq_cdev = kzalloc(sizeof(*cpufreq_cdev), GFP_KERNEL);
+ cpufreq_cdev = kzalloc_obj(*cpufreq_cdev);
if (!cpufreq_cdev)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/thermal/cpuidle_cooling.c b/drivers/thermal/cpuidle_cooling.c
index f678c1281862..425f596614e8 100644
--- a/drivers/thermal/cpuidle_cooling.c
+++ b/drivers/thermal/cpuidle_cooling.c
@@ -179,7 +179,7 @@ static int __cpuidle_cooling_register(struct device_node *np,
char *name;
int ret;
- idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL);
+ idle_cdev = kzalloc_obj(*idle_cdev);
if (!idle_cdev) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/thermal/da9062-thermal.c b/drivers/thermal/da9062-thermal.c
index 2077e85ef5ca..a8d4b766ba21 100644
--- a/drivers/thermal/da9062-thermal.c
+++ b/drivers/thermal/da9062-thermal.c
@@ -137,7 +137,7 @@ static int da9062_thermal_get_temp(struct thermal_zone_device *z,
return 0;
}
-static struct thermal_zone_device_ops da9062_thermal_ops = {
+static const struct thermal_zone_device_ops da9062_thermal_ops = {
.get_temp = da9062_thermal_get_temp,
};
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c
index 8fd7cf1932cd..1c7dffc8d45f 100644
--- a/drivers/thermal/devfreq_cooling.c
+++ b/drivers/thermal/devfreq_cooling.c
@@ -402,7 +402,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
int err, num_opps;
- dfc = kzalloc(sizeof(*dfc), GFP_KERNEL);
+ dfc = kzalloc_obj(*dfc);
if (!dfc)
return ERR_PTR(-ENOMEM);
@@ -472,7 +472,8 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
remove_qos_req:
dev_pm_qos_remove_request(&dfc->req_max_freq);
free_table:
- kfree(dfc->freq_table);
+ if (!dfc->em_pd)
+ kfree(dfc->freq_table);
free_dfc:
kfree(dfc);
diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c
index f9157a47156b..723bc72f0626 100644
--- a/drivers/thermal/dove_thermal.c
+++ b/drivers/thermal/dove_thermal.c
@@ -106,7 +106,7 @@ static int dove_get_temp(struct thermal_zone_device *thermal,
return 0;
}
-static struct thermal_zone_device_ops ops = {
+static const struct thermal_zone_device_ops ops = {
.get_temp = dove_get_temp,
};
diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c
index 0d9f636c80f4..37f2e22a999e 100644
--- a/drivers/thermal/gov_power_allocator.c
+++ b/drivers/thermal/gov_power_allocator.c
@@ -622,8 +622,7 @@ static int allocate_actors_buffer(struct power_allocator_params *params,
goto clean_state;
}
- params->power = kcalloc(num_actors, sizeof(struct power_actor),
- GFP_KERNEL);
+ params->power = kzalloc_objs(struct power_actor, num_actors);
if (!params->power) {
ret = -ENOMEM;
goto clean_state;
@@ -699,7 +698,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
struct power_allocator_params *params;
int ret;
- params = kzalloc(sizeof(*params), GFP_KERNEL);
+ params = kzalloc_obj(*params);
if (!params)
return -ENOMEM;
@@ -720,7 +719,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
}
if (!tz->tzp) {
- tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL);
+ tz->tzp = kzalloc_obj(*tz->tzp);
if (!tz->tzp) {
ret = -ENOMEM;
goto free_params;
diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c
index d1bb59f1dfbd..ea277c466d8d 100644
--- a/drivers/thermal/gov_step_wise.c
+++ b/drivers/thermal/gov_step_wise.c
@@ -20,11 +20,13 @@
* If the temperature is higher than a trip point,
* a. if the trend is THERMAL_TREND_RAISING, use higher cooling
* state for this trip point
- * b. if the trend is THERMAL_TREND_DROPPING, do nothing
+ * b. if the trend is THERMAL_TREND_DROPPING, use a lower cooling state
+ * for this trip point, but keep the cooling state above the applicable
+ * minimum
* If the temperature is lower than a trip point,
* a. if the trend is THERMAL_TREND_RAISING, do nothing
- * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
- * state for this trip point, if the cooling state already
+ * b. if the trend is THERMAL_TREND_DROPPING, use the minimum applicable
+ * cooling state for this trip point, or if the cooling state already
* equals lower limit, deactivate the thermal instance
*/
static unsigned long get_target_state(struct thermal_instance *instance,
@@ -51,6 +53,17 @@ static unsigned long get_target_state(struct thermal_instance *instance,
if (throttle) {
if (trend == THERMAL_TREND_RAISING)
return clamp(cur_state + 1, instance->lower, instance->upper);
+
+ /*
+ * If the zone temperature is falling, the cooling level can
+ * be reduced, but it should still be above the lower state of
+ * the given thermal instance to pull the temperature further
+ * down.
+ */
+ if (trend == THERMAL_TREND_DROPPING)
+ return clamp(cur_state - 1,
+ min(instance->lower + 1, instance->upper),
+ instance->upper);
} else if (trend == THERMAL_TREND_DROPPING) {
if (cur_state <= instance->lower)
return THERMAL_NO_TARGET;
@@ -69,16 +82,14 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz,
const struct thermal_trip_desc *td,
int trip_threshold)
{
+ bool throttle = tz->temperature >= trip_threshold;
const struct thermal_trip *trip = &td->trip;
enum thermal_trend trend = get_tz_trend(tz, trip);
int trip_id = thermal_zone_trip_id(tz, trip);
struct thermal_instance *instance;
- bool throttle = false;
- if (tz->temperature >= trip_threshold) {
- throttle = true;
+ if (throttle)
trace_thermal_zone_trip(tz, trip_id, trip->type);
- }
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
trip_id, trip->type, trip_threshold, trend, throttle);
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
index 7e918bd3f100..4307161533a7 100644
--- a/drivers/thermal/hisi_thermal.c
+++ b/drivers/thermal/hisi_thermal.c
@@ -412,8 +412,8 @@ static int hi3660_thermal_probe(struct hisi_thermal_data *data)
data->nr_sensors = 1;
- data->sensor = devm_kzalloc(dev, sizeof(*data->sensor) *
- data->nr_sensors, GFP_KERNEL);
+ data->sensor = devm_kcalloc(dev, data->nr_sensors,
+ sizeof(*data->sensor), GFP_KERNEL);
if (!data->sensor)
return -ENOMEM;
diff --git a/drivers/thermal/imx91_thermal.c b/drivers/thermal/imx91_thermal.c
new file mode 100644
index 000000000000..25915bb702be
--- /dev/null
+++ b/drivers/thermal/imx91_thermal.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/thermal.h>
+#include <linux/units.h>
+
+#include "thermal_hwmon.h"
+
+#define REG_SET 0x4
+#define REG_CLR 0x8
+#define REG_TOG 0xc
+
+#define IMX91_TMU_CTRL0 0x0
+#define IMX91_TMU_CTRL0_THR1_IE BIT(9)
+#define IMX91_TMU_CTRL0_THR1_MASK GENMASK(3, 2)
+#define IMX91_TMU_CTRL0_CLR_FLT1 BIT(21)
+
+#define IMX91_TMU_THR_MODE_LE 0
+#define IMX91_TMU_THR_MODE_GE 1
+
+#define IMX91_TMU_STAT0 0x10
+#define IMX91_TMU_STAT0_THR1_IF BIT(9)
+#define IMX91_TMU_STAT0_THR1_STAT BIT(13)
+#define IMX91_TMU_STAT0_DRDY0_IF_MASK BIT(16)
+
+#define IMX91_TMU_DATA0 0x20
+
+#define IMX91_TMU_CTRL1 0x200
+#define IMX91_TMU_CTRL1_EN BIT(31)
+#define IMX91_TMU_CTRL1_START BIT(30)
+#define IMX91_TMU_CTRL1_STOP BIT(29)
+#define IMX91_TMU_CTRL1_RES_MASK GENMASK(19, 18)
+#define IMX91_TMU_CTRL1_MEAS_MODE_MASK GENMASK(25, 24)
+#define IMX91_TMU_CTRL1_MEAS_MODE_SINGLE 0
+#define IMX91_TMU_CTRL1_MEAS_MODE_CONTINUES 1
+#define IMX91_TMU_CTRL1_MEAS_MODE_PERIODIC 2
+
+#define IMX91_TMU_THR_CTRL01 0x30
+#define IMX91_TMU_THR_CTRL01_THR1_MASK GENMASK(31, 16)
+
+#define IMX91_TMU_REF_DIV 0x280
+#define IMX91_TMU_DIV_EN BIT(31)
+#define IMX91_TMU_DIV_MASK GENMASK(23, 16)
+#define IMX91_TMU_DIV_MAX 255
+
+#define IMX91_TMU_PUD_ST_CTRL 0x2b0
+#define IMX91_TMU_PUDL_MASK GENMASK(23, 16)
+
+#define IMX91_TMU_TRIM1 0x2e0
+#define IMX91_TMU_TRIM2 0x2f0
+
+#define IMX91_TMU_TEMP_LOW_LIMIT -40000
+#define IMX91_TMU_TEMP_HIGH_LIMIT 125000
+
+#define IMX91_TMU_DEFAULT_TRIM1_CONFIG 0xb561bc2d
+#define IMX91_TMU_DEFAULT_TRIM2_CONFIG 0x65d4
+
+#define IMX91_TMU_PERIOD_CTRL 0x270
+#define IMX91_TMU_PERIOD_CTRL_MEAS_MASK GENMASK(23, 0)
+
+#define IMX91_TMP_FRAC 64
+
+struct imx91_tmu {
+ void __iomem *base;
+ struct clk *clk;
+ struct device *dev;
+ struct thermal_zone_device *tzd;
+};
+
+static void imx91_tmu_start(struct imx91_tmu *tmu, bool start)
+{
+ u32 val = start ? IMX91_TMU_CTRL1_START : IMX91_TMU_CTRL1_STOP;
+
+ writel_relaxed(val, tmu->base + IMX91_TMU_CTRL1 + REG_SET);
+}
+
+static void imx91_tmu_enable(struct imx91_tmu *tmu, bool enable)
+{
+ u32 reg = IMX91_TMU_CTRL1;
+
+ reg += enable ? REG_SET : REG_CLR;
+
+ writel_relaxed(IMX91_TMU_CTRL1_EN, tmu->base + reg);
+}
+
+static int imx91_tmu_to_mcelsius(int x)
+{
+ return x * MILLIDEGREE_PER_DEGREE / IMX91_TMP_FRAC;
+}
+
+static int imx91_tmu_from_mcelsius(int x)
+{
+ return x * IMX91_TMP_FRAC / MILLIDEGREE_PER_DEGREE;
+}
+
+static int imx91_tmu_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
+ s16 data;
+
+ /* DATA0 is 16bit signed number */
+ data = readw_relaxed(tmu->base + IMX91_TMU_DATA0);
+ *temp = imx91_tmu_to_mcelsius(data);
+
+ return 0;
+}
+
+static int imx91_tmu_set_trips(struct thermal_zone_device *tz, int low, int high)
+{
+ struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
+ int val;
+
+ if (high >= IMX91_TMU_TEMP_HIGH_LIMIT)
+ return -EINVAL;
+
+ writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
+
+ /* Comparator1 for temperature threshold */
+ writel_relaxed(IMX91_TMU_THR_CTRL01_THR1_MASK, tmu->base + IMX91_TMU_THR_CTRL01 + REG_CLR);
+ val = FIELD_PREP(IMX91_TMU_THR_CTRL01_THR1_MASK, imx91_tmu_from_mcelsius(high));
+
+ writel_relaxed(val, tmu->base + IMX91_TMU_THR_CTRL01 + REG_SET);
+
+ writel_relaxed(IMX91_TMU_STAT0_THR1_IF, tmu->base + IMX91_TMU_STAT0 + REG_CLR);
+
+ writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_SET);
+
+ return 0;
+}
+
+static int imx91_init_from_nvmem_cells(struct imx91_tmu *tmu)
+{
+ struct device *dev = tmu->dev;
+ u32 trim1, trim2;
+ int ret;
+
+ ret = nvmem_cell_read_u32(dev, "trim1", &trim1);
+ if (ret)
+ return ret;
+
+ ret = nvmem_cell_read_u32(dev, "trim2", &trim2);
+ if (ret)
+ return ret;
+
+ if (trim1 == 0 || trim2 == 0)
+ return -EINVAL;
+
+ writel_relaxed(trim1, tmu->base + IMX91_TMU_TRIM1);
+ writel_relaxed(trim2, tmu->base + IMX91_TMU_TRIM2);
+
+ return 0;
+}
+
+static void imx91_tmu_action_remove(void *data)
+{
+ struct imx91_tmu *tmu = data;
+
+ /* disable tmu */
+ imx91_tmu_enable(tmu, false);
+}
+
+static irqreturn_t imx91_tmu_alarm_irq(int irq, void *data)
+{
+ struct imx91_tmu *tmu = data;
+ u32 val;
+
+ val = readl_relaxed(tmu->base + IMX91_TMU_STAT0);
+
+ /* Check if comparison interrupt occurred */
+ if (val & IMX91_TMU_STAT0_THR1_IF) {
+ /* Clear irq flag and disable interrupt until reconfigured */
+ writel(IMX91_TMU_STAT0_THR1_IF, tmu->base + IMX91_TMU_STAT0 + REG_CLR);
+ writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
+
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t imx91_tmu_alarm_irq_thread(int irq, void *data)
+{
+ struct imx91_tmu *tmu = data;
+
+ thermal_zone_device_update(tmu->tzd, THERMAL_EVENT_UNSPECIFIED);
+
+ return IRQ_HANDLED;
+}
+
+static int imx91_tmu_change_mode(struct thermal_zone_device *tz, enum thermal_device_mode mode)
+{
+ struct imx91_tmu *tmu = thermal_zone_device_priv(tz);
+ int ret;
+
+ if (mode == THERMAL_DEVICE_ENABLED) {
+ ret = pm_runtime_get(tmu->dev);
+ if (ret < 0)
+ return ret;
+
+ writel_relaxed(IMX91_TMU_CTRL0_THR1_IE | IMX91_TMU_CTRL0_THR1_MASK,
+ tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
+
+ writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL0_THR1_MASK, IMX91_TMU_THR_MODE_GE),
+ tmu->base + IMX91_TMU_CTRL0 + REG_SET);
+ imx91_tmu_start(tmu, true);
+ } else {
+ writel_relaxed(IMX91_TMU_CTRL0_THR1_IE, tmu->base + IMX91_TMU_CTRL0 + REG_CLR);
+ imx91_tmu_start(tmu, false);
+ pm_runtime_put(tmu->dev);
+ }
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops tmu_tz_ops = {
+ .get_temp = imx91_tmu_get_temp,
+ .change_mode = imx91_tmu_change_mode,
+ .set_trips = imx91_tmu_set_trips,
+};
+
+static int imx91_tmu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx91_tmu *tmu;
+ unsigned long rate;
+ int irq, ret;
+ u32 div;
+
+ tmu = devm_kzalloc(dev, sizeof(struct imx91_tmu), GFP_KERNEL);
+ if (!tmu)
+ return -ENOMEM;
+
+ tmu->dev = dev;
+
+ tmu->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(tmu->base))
+ return dev_err_probe(dev, PTR_ERR(tmu->base), "failed to get io resource");
+
+ tmu->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(tmu->clk))
+ return dev_err_probe(dev, PTR_ERR(tmu->clk), "failed to get tmu clock\n");
+
+ platform_set_drvdata(pdev, tmu);
+
+ /* disable the monitor during initialization */
+ imx91_tmu_enable(tmu, false);
+ imx91_tmu_start(tmu, false);
+
+ ret = imx91_init_from_nvmem_cells(tmu);
+ if (ret) {
+ dev_warn(dev, "can't get trim value, use default settings\n");
+
+ writel_relaxed(IMX91_TMU_DEFAULT_TRIM1_CONFIG, tmu->base + IMX91_TMU_TRIM1);
+ writel_relaxed(IMX91_TMU_DEFAULT_TRIM2_CONFIG, tmu->base + IMX91_TMU_TRIM2);
+ }
+
+ /* The typical conv clk is 4MHz, the output freq is 'rate / (div + 1)' */
+ rate = clk_get_rate(tmu->clk);
+ div = (rate / (4 * HZ_PER_MHZ)) - 1;
+ if (div > IMX91_TMU_DIV_MAX)
+ return dev_err_probe(dev, -EINVAL, "clock divider exceed hardware limitation");
+
+ /* Set divider value and enable divider */
+ writel_relaxed(IMX91_TMU_DIV_EN | FIELD_PREP(IMX91_TMU_DIV_MASK, div),
+ tmu->base + IMX91_TMU_REF_DIV);
+
+ /* Set max power up delay: 'Tpud(ms) = 0xFF * 1000 / 4000000' */
+ writel_relaxed(FIELD_PREP(IMX91_TMU_PUDL_MASK, 100U), tmu->base + IMX91_TMU_PUD_ST_CTRL);
+
+ /*
+ * Set resolution mode
+ * 00b - Conversion time = 0.59325 ms
+ * 01b - Conversion time = 1.10525 ms
+ * 10b - Conversion time = 2.12925 ms
+ * 11b - Conversion time = 4.17725 ms
+ */
+ writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_RES_MASK, 0x3),
+ tmu->base + IMX91_TMU_CTRL1 + REG_CLR);
+ writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_RES_MASK, 0x1),
+ tmu->base + IMX91_TMU_CTRL1 + REG_SET);
+
+ writel_relaxed(IMX91_TMU_CTRL1_MEAS_MODE_MASK, tmu->base + IMX91_TMU_CTRL1 + REG_CLR);
+ writel_relaxed(FIELD_PREP(IMX91_TMU_CTRL1_MEAS_MODE_MASK,
+ IMX91_TMU_CTRL1_MEAS_MODE_PERIODIC),
+ tmu->base + IMX91_TMU_CTRL1 + REG_SET);
+
+ /*
+ * Set Periodic Measurement Frequency to 25Hz:
+ * tMEAS_FREQ = tCONV_CLK * PERIOD_CTRL[MEAS_FREQ]
+ */
+ writel_relaxed(FIELD_PREP(IMX91_TMU_PERIOD_CTRL_MEAS_MASK, 4 * HZ_PER_MHZ / 25),
+ tmu->base + IMX91_TMU_PERIOD_CTRL);
+
+ imx91_tmu_enable(tmu, true);
+ ret = devm_add_action(dev, imx91_tmu_action_remove, tmu);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failure to add action imx91_tmu_action_remove()\n");
+
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ tmu->tzd = devm_thermal_of_zone_register(dev, 0, tmu, &tmu_tz_ops);
+ if (IS_ERR(tmu->tzd))
+ return dev_err_probe(dev, PTR_ERR(tmu->tzd),
+ "failed to register thermal zone sensor\n");
+
+ devm_thermal_add_hwmon_sysfs(dev, tmu->tzd);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, imx91_tmu_alarm_irq,
+ imx91_tmu_alarm_irq_thread,
+ IRQF_ONESHOT, "imx91_thermal", tmu);
+
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to request alarm irq\n");
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int imx91_tmu_runtime_suspend(struct device *dev)
+{
+ struct imx91_tmu *tmu = dev_get_drvdata(dev);
+
+ /* disable tmu */
+ imx91_tmu_enable(tmu, false);
+
+ clk_disable_unprepare(tmu->clk);
+
+ return 0;
+}
+
+static int imx91_tmu_runtime_resume(struct device *dev)
+{
+ struct imx91_tmu *tmu = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(tmu->clk);
+ if (ret)
+ return ret;
+
+ imx91_tmu_enable(tmu, true);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx91_tmu_pm_ops, imx91_tmu_runtime_suspend,
+ imx91_tmu_runtime_resume, NULL);
+
+static const struct of_device_id imx91_tmu_table[] = {
+ { .compatible = "fsl,imx91-tmu", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, imx91_tmu_table);
+
+static struct platform_driver imx91_tmu = {
+ .driver = {
+ .name = "imx91_thermal",
+ .pm = pm_ptr(&imx91_tmu_pm_ops),
+ .of_match_table = imx91_tmu_table,
+ },
+ .probe = imx91_tmu_probe,
+};
+module_platform_driver(imx91_tmu);
+
+MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
+MODULE_DESCRIPTION("i.MX91 Thermal Monitor Unit driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index bab52e6b3b15..38c993d1bcb3 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -361,7 +361,7 @@ static bool imx_should_bind(struct thermal_zone_device *tz,
return trip->type == THERMAL_TRIP_PASSIVE;
}
-static struct thermal_zone_device_ops imx_tz_ops = {
+static const struct thermal_zone_device_ops imx_tz_ops = {
.should_bind = imx_should_bind,
.get_temp = imx_get_temp,
.change_mode = imx_change_mode,
diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig
index e0268fac7093..347c59bc87d6 100644
--- a/drivers/thermal/intel/Kconfig
+++ b/drivers/thermal/intel/Kconfig
@@ -44,7 +44,8 @@ config INTEL_SOC_DTS_IOSF_CORE
config INTEL_SOC_DTS_THERMAL
tristate "Intel SoCs DTS thermal driver"
- depends on X86 && PCI && ACPI
+ depends on X86_64 && PCI && ACPI && NET
+ select INT340X_THERMAL
select INTEL_SOC_DTS_IOSF_CORE
help
Enable this to register Intel SoCs (e.g. Bay Trail) platform digital
diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig
index 4c699f0896b5..4ced7bdcd62c 100644
--- a/drivers/thermal/intel/int340x_thermal/Kconfig
+++ b/drivers/thermal/intel/int340x_thermal/Kconfig
@@ -12,6 +12,7 @@ config INT340X_THERMAL
select ACPI_THERMAL_LIB
select INTEL_SOC_DTS_IOSF_CORE
select INTEL_TCC
+ select ACPI_PLATFORM_PROFILE
select PROC_THERMAL_MMIO_RAPL if POWERCAP
help
Newer laptops and tablets that use ACPI may have thermal sensors and
diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile
index fe3f43924525..436be34b21a9 100644
--- a/drivers/thermal/intel/int340x_thermal/Makefile
+++ b/drivers/thermal/intel/int340x_thermal/Makefile
@@ -9,9 +9,11 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci_legacy.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci.o
obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_rfim.o
+obj-$(CONFIG_INT340X_THERMAL) += platform_temperature_control.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_power_floor.o
+obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_soc_slider.o
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
index cb149bcdd7d5..0a548fdc4a7e 100644
--- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
@@ -89,7 +89,7 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
}
*trt_count = p->package.count;
- trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL);
+ trts = kzalloc_objs(struct trt, *trt_count);
if (!trts) {
result = -ENOMEM;
goto end;
@@ -165,7 +165,7 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
/* ignore p->package.elements[0], as this is _ART Revision field */
*art_count = p->package.count - 1;
- arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL);
+ arts = kzalloc_objs(struct art, *art_count);
if (!arts) {
result = -ENOMEM;
goto end;
@@ -220,9 +220,6 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps
int i, result = 0;
struct psvt *psvts;
- if (!acpi_has_method(handle, "PSVT"))
- return -ENODEV;
-
status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -256,7 +253,7 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps
goto end;
}
- psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);
+ psvts = kzalloc_objs(*psvts, *psvt_count);
if (!psvts) {
result = -ENOMEM;
goto end;
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
index 0e07693ecf59..d200734625ee 100644
--- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -16,6 +16,8 @@
#define INT3400_ODVP_CHANGED 0x88
#define INT3400_KEEP_ALIVE 0xA0
#define INT3400_FAKE_TEMP (20 * 1000) /* faked temp sensor with 20C */
+/* UUID prefix length for comparison - sufficient for all UUIDs */
+#define INT3400_UUID_PREFIX_LEN 7
enum int3400_thermal_uuid {
INT3400_THERMAL_ACTIVE = 0,
@@ -112,7 +114,7 @@ static ssize_t available_uuids_show(struct device *dev,
int length = 0;
if (!priv->uuid_bitmap)
- return sprintf(buf, "UNKNOWN\n");
+ return sysfs_emit(buf, "UNKNOWN\n");
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
if (priv->uuid_bitmap & (1 << i))
@@ -129,7 +131,7 @@ static ssize_t current_uuid_show(struct device *dev,
int i, length = 0;
if (priv->current_uuid_index >= 0)
- return sprintf(buf, "%s\n",
+ return sysfs_emit(buf, "%s\n",
int3400_thermal_uuids[priv->current_uuid_index]);
for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
@@ -140,7 +142,7 @@ static ssize_t current_uuid_show(struct device *dev,
if (length)
return length;
- return sprintf(buf, "INVALID\n");
+ return sysfs_emit(buf, "INVALID\n");
}
static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable)
@@ -199,7 +201,7 @@ static ssize_t current_uuid_store(struct device *dev,
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
if (!strncmp(buf, int3400_thermal_uuids[i],
- sizeof(int3400_thermal_uuids[i]) - 1)) {
+ INT3400_UUID_PREFIX_LEN)) {
/*
* If we have a list of supported UUIDs, make sure
* this one is supported.
@@ -340,7 +342,7 @@ static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
odvp_attr = container_of(attr, struct odvp_attr, attr);
- return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
+ return sysfs_emit(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
}
static void cleanup_odvp(struct int3400_thermal_priv *priv)
@@ -380,8 +382,7 @@ static int evaluate_odvp(struct int3400_thermal_priv *priv)
if (priv->odvp == NULL) {
priv->odvp_count = obj->package.count;
- priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int),
- GFP_KERNEL);
+ priv->odvp = kmalloc_objs(int, priv->odvp_count);
if (!priv->odvp) {
ret = -ENOMEM;
goto out_err;
@@ -389,9 +390,8 @@ static int evaluate_odvp(struct int3400_thermal_priv *priv)
}
if (priv->odvp_attrs == NULL) {
- priv->odvp_attrs = kcalloc(priv->odvp_count,
- sizeof(struct odvp_attr),
- GFP_KERNEL);
+ priv->odvp_attrs = kzalloc_objs(struct odvp_attr,
+ priv->odvp_count);
if (!priv->odvp_attrs) {
ret = -ENOMEM;
goto out_err;
@@ -515,7 +515,7 @@ eval_odvp:
return result;
}
-static struct thermal_zone_device_ops int3400_thermal_ops = {
+static const struct thermal_zone_device_ops int3400_thermal_ops = {
.get_temp = int3400_thermal_get_temp,
.change_mode = int3400_thermal_change_mode,
};
@@ -561,7 +561,7 @@ static int int3400_thermal_probe(struct platform_device *pdev)
if (!adev)
return -ENODEV;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
@@ -690,6 +690,8 @@ static const struct acpi_device_id int3400_thermal_match[] = {
{"INTC1068", 0},
{"INTC10A0", 0},
{"INTC10D4", 0},
+ {"INTC10FC", 0},
+ {"INTC10F3", 0},
{}
};
diff --git a/drivers/thermal/intel/int340x_thermal/int3402_thermal.c b/drivers/thermal/intel/int340x_thermal/int3402_thermal.c
index 543b03960e99..57b90005888a 100644
--- a/drivers/thermal/intel/int340x_thermal/int3402_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3402_thermal.c
@@ -45,6 +45,9 @@ static int int3402_thermal_probe(struct platform_device *pdev)
struct int3402_thermal_data *d;
int ret;
+ if (!adev)
+ return -ENODEV;
+
if (!acpi_has_method(adev->handle, "_TMP"))
return -ENODEV;
diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
index 5a925a8df7b3..264c9bc8e645 100644
--- a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
@@ -276,6 +276,8 @@ static const struct acpi_device_id int3403_device_ids[] = {
{"INTC1069", 0},
{"INTC10A1", 0},
{"INTC10D5", 0},
+ {"INTC10FD", 0},
+ {"INTC10F4", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
index 8dca6a6aceca..965700ee6962 100644
--- a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
@@ -123,7 +123,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
acpi_status status;
int i, ret;
- int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL);
+ int34x_zone = kzalloc_obj(*int34x_zone);
if (!int34x_zone)
return ERR_PTR(-ENOMEM);
@@ -133,8 +133,8 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
if (ACPI_SUCCESS(status))
int34x_zone->aux_trip_nr = trip_cnt;
- zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT),
- GFP_KERNEL);
+ zone_trips = kzalloc_objs(*zone_trips,
+ trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT);
if (!zone_trips) {
ret = -ENOMEM;
goto err_trips_alloc;
@@ -143,7 +143,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
for (i = 0; i < trip_cnt; i++) {
zone_trips[i].type = THERMAL_TRIP_PASSIVE;
zone_trips[i].temperature = THERMAL_TEMP_INVALID;
- zone_trips[i].flags |= THERMAL_TRIP_FLAG_RW_TEMP;
+ zone_trips[i].flags = THERMAL_TRIP_FLAG_RW_TEMP;
zone_trips[i].priv = THERMAL_INT_TO_TRIP_PRIV(i);
}
diff --git a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c
new file mode 100644
index 000000000000..0ccc72c93499
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * processor thermal device platform temperature controls
+ * Copyright (c) 2025, Intel Corporation.
+ */
+
+/*
+ * Platform temperature controls hardware interface
+ *
+ * The hardware control interface is via MMIO offsets in the processor
+ * thermal device MMIO space. There are three instances of MMIO registers.
+ * All registers are 64 bit wide with RW access.
+ *
+ * Name: PLATFORM_TEMPERATURE_CONTROL
+ * Offsets: 0x5B20, 0x5B28, 0x5B30
+ *
+ * Bits Description
+ * 7:0 TARGET_TEMP : Target temperature limit to which the control
+ * mechanism is regulating. Units: 0.5C.
+ * 8:8 ENABLE: Read current enable status of the feature or enable
+ * feature.
+ * 11:9 GAIN: Sets the aggressiveness of control loop from 0 to 7
+ * 7 graceful, favors performance at the expense of temperature
+ * overshoots
+ * 0 aggressive, favors tight regulation over performance
+ * 12:12 TEMPERATURE_OVERRIDE_EN
+ * When set, hardware will use TEMPERATURE_OVERRIDE values instead
+ * of reading from corresponding sensor.
+ * 15:13 RESERVED
+ * 23:16 MIN_PERFORMANCE_LEVEL: Minimum Performance level below which the
+ * there will be no throttling. 0 - all levels of throttling allowed
+ * including survivability actions. 255 - no throttling allowed.
+ * 31:24 TEMPERATURE_OVERRIDE: Allows SW to override the input temperature.
+ * hardware will use this value instead of the sensor temperature.
+ * Units: 0.5C.
+ * 63:32 RESERVED
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+struct mmio_reg {
+ int bits;
+ u16 mask;
+ u16 shift;
+ u16 units;
+};
+
+#define MAX_ATTR_GROUP_NAME_LEN 32
+#define PTC_MAX_ATTRS 4
+
+struct ptc_data {
+ u32 offset;
+ struct pci_dev *pdev;
+ struct attribute_group ptc_attr_group;
+ struct attribute *ptc_attrs[PTC_MAX_ATTRS];
+ struct device_attribute temperature_target_attr;
+ struct device_attribute enable_attr;
+ struct device_attribute thermal_tolerance_attr;
+ char group_name[MAX_ATTR_GROUP_NAME_LEN];
+};
+
+static const struct mmio_reg ptc_mmio_regs[] = {
+ { 8, 0xFF, 0, 500}, /* temperature_target, units 0.5C*/
+ { 1, 0x01, 8, 0}, /* enable */
+ { 3, 0x7, 9, 0}, /* gain */
+ { 8, 0xFF, 16, 0}, /* min_performance_level */
+ { 1, 0x1, 12, 0}, /* temperature_override_enable */
+ { 8, 0xFF, 24, 500}, /* temperature_override, units 0.5C */
+};
+
+#define PTC_MAX_INSTANCES 3
+
+/* Unique offset for each PTC instance */
+static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30};
+
+/* These will represent sysfs attribute names */
+static const char * const ptc_strings[] = {
+ "temperature_target",
+ "enable",
+ "thermal_tolerance",
+ NULL
+};
+
+/* Lock to protect concurrent read/write and read-modify-write */
+static DEFINE_MUTEX(ptc_lock);
+
+static ssize_t ptc_mmio_show(struct ptc_data *data, struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct proc_thermal_device *proc_priv;
+ const struct mmio_reg *mmio_regs;
+ int ret, units;
+ u64 reg_val;
+
+ proc_priv = pci_get_drvdata(pdev);
+ mmio_regs = ptc_mmio_regs;
+ ret = match_string(ptc_strings, -1, attr->attr.name);
+ if (ret < 0)
+ return ret;
+
+ units = mmio_regs[ret].units;
+
+ guard(mutex)(&ptc_lock);
+
+ reg_val = readq((void __iomem *) (proc_priv->mmio_base + data->offset));
+ ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;
+ if (units)
+ ret *= units;
+
+ return sysfs_emit(buf, "%d\n", ret);
+}
+
+#define PTC_SHOW(suffix)\
+static ssize_t suffix##_show(struct device *dev,\
+ struct device_attribute *attr,\
+ char *buf)\
+{\
+ struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
+ return ptc_mmio_show(data, dev, attr, buf);\
+}
+
+static void ptc_mmio_write(struct pci_dev *pdev, u32 offset, int index, u32 value)
+{
+ struct proc_thermal_device *proc_priv;
+ u64 mask, reg_val;
+
+ proc_priv = pci_get_drvdata(pdev);
+
+ mask = GENMASK_ULL(ptc_mmio_regs[index].shift + ptc_mmio_regs[index].bits - 1,
+ ptc_mmio_regs[index].shift);
+
+ guard(mutex)(&ptc_lock);
+
+ reg_val = readq((void __iomem *) (proc_priv->mmio_base + offset));
+ reg_val &= ~mask;
+ reg_val |= (value << ptc_mmio_regs[index].shift);
+ writeq(reg_val, (void __iomem *) (proc_priv->mmio_base + offset));
+}
+
+static int ptc_store(struct ptc_data *data, struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ unsigned int input;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &input);
+ if (ret)
+ return ret;
+
+ ret = match_string(ptc_strings, -1, attr->attr.name);
+ if (ret < 0)
+ return ret;
+
+ if (ptc_mmio_regs[ret].units)
+ input /= ptc_mmio_regs[ret].units;
+
+ if (input > ptc_mmio_regs[ret].mask)
+ return -EINVAL;
+
+ ptc_mmio_write(pdev, data->offset, ret, input);
+
+ return count;
+}
+
+#define PTC_STORE(suffix)\
+static ssize_t suffix##_store(struct device *dev,\
+ struct device_attribute *attr,\
+ const char *buf, size_t count)\
+{\
+ struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
+ return ptc_store(data, dev, attr, buf, count);\
+}
+
+PTC_SHOW(temperature_target);
+PTC_STORE(temperature_target);
+PTC_SHOW(enable);
+PTC_STORE(enable);
+PTC_SHOW(thermal_tolerance);
+PTC_STORE(thermal_tolerance);
+
+#define ptc_init_attribute(_name)\
+ do {\
+ sysfs_attr_init(&data->_name##_attr.attr);\
+ data->_name##_attr.show = _name##_show;\
+ data->_name##_attr.store = _name##_store;\
+ data->_name##_attr.attr.name = #_name;\
+ data->_name##_attr.attr.mode = 0644;\
+ } while (0)
+
+static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data *data)
+{
+ int ret, index = 0;
+
+ ptc_init_attribute(temperature_target);
+ ptc_init_attribute(enable);
+ ptc_init_attribute(thermal_tolerance);
+
+ data->ptc_attrs[index++] = &data->temperature_target_attr.attr;
+ data->ptc_attrs[index++] = &data->enable_attr.attr;
+ data->ptc_attrs[index++] = &data->thermal_tolerance_attr.attr;
+ data->ptc_attrs[index] = NULL;
+
+ snprintf(data->group_name, MAX_ATTR_GROUP_NAME_LEN,
+ "ptc_%d_control", instance);
+ data->ptc_attr_group.name = data->group_name;
+ data->ptc_attr_group.attrs = data->ptc_attrs;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &data->ptc_attr_group);
+
+ return ret;
+}
+
+static struct ptc_data ptc_instance[PTC_MAX_INSTANCES];
+static struct dentry *ptc_debugfs;
+
+#define PTC_TEMP_OVERRIDE_ENABLE_INDEX 4
+#define PTC_TEMP_OVERRIDE_INDEX 5
+
+static ssize_t ptc_temperature_write(struct file *file, const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct ptc_data *ptc_instance = file->private_data;
+ struct pci_dev *pdev = ptc_instance->pdev;
+ char buf[32];
+ ssize_t len;
+ u32 value;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, data, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (kstrtouint(buf, 0, &value))
+ return -EINVAL;
+
+ if (ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units)
+ value /= ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units;
+
+ if (value > ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].mask)
+ return -EINVAL;
+
+ if (!value) {
+ ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 0);
+ } else {
+ ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_INDEX, value);
+ ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 1);
+ }
+
+ return count;
+}
+
+static const struct file_operations ptc_fops = {
+ .open = simple_open,
+ .write = ptc_temperature_write,
+ .llseek = generic_file_llseek,
+};
+
+static void ptc_create_debugfs(void)
+{
+ ptc_debugfs = debugfs_create_dir("platform_temperature_control", NULL);
+
+ debugfs_create_file("temperature_0", 0200, ptc_debugfs, &ptc_instance[0], &ptc_fops);
+ debugfs_create_file("temperature_1", 0200, ptc_debugfs, &ptc_instance[1], &ptc_fops);
+ debugfs_create_file("temperature_2", 0200, ptc_debugfs, &ptc_instance[2], &ptc_fops);
+}
+
+static void ptc_delete_debugfs(void)
+{
+ debugfs_remove_recursive(ptc_debugfs);
+}
+
+int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+ if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
+ int i;
+
+ for (i = 0; i < PTC_MAX_INSTANCES; i++) {
+ ptc_instance[i].offset = ptc_offsets[i];
+ ptc_instance[i].pdev = pdev;
+ ptc_create_groups(pdev, i, &ptc_instance[i]);
+ }
+
+ ptc_create_debugfs();
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(proc_thermal_ptc_add);
+
+void proc_thermal_ptc_remove(struct pci_dev *pdev)
+{
+ struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
+
+ if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
+ int i;
+
+ for (i = 0; i < PTC_MAX_INSTANCES; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &ptc_instance[i].ptc_attr_group);
+
+ ptc_delete_debugfs();
+ }
+}
+EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove);
+
+MODULE_IMPORT_NS("INT340X_THERMAL");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Processor Thermal PTC Interface");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
index c868d8b7bd1c..f80dbe2ca7e4 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -8,7 +8,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/sysfs.h>
#include <linux/thermal.h>
+#include <asm/msr.h>
#include "int340x_thermal_zone.h"
#include "processor_thermal_device.h"
#include "../intel_soc_dts_iosf.h"
@@ -22,7 +24,7 @@ static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \
{ \
struct proc_thermal_device *proc_dev = dev_get_drvdata(dev); \
\
- return sprintf(buf, "%lu\n",\
+ return sysfs_emit(buf, "%lu\n",\
(unsigned long)proc_dev->power_limits[index].suffix * 1000); \
}
@@ -142,7 +144,7 @@ static ssize_t tcc_offset_degree_celsius_show(struct device *dev,
if (offset < 0)
return offset;
- return sprintf(buf, "%d\n", offset);
+ return sysfs_emit(buf, "%d\n", offset);
}
static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
@@ -153,7 +155,7 @@ static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
u64 val;
int err;
- err = rdmsrl_safe(MSR_PLATFORM_INFO, &val);
+ err = rdmsrq_safe(MSR_PLATFORM_INFO, &val);
if (err)
return err;
@@ -337,10 +339,17 @@ static int tcc_offset_save = -1;
int proc_thermal_suspend(struct device *dev)
{
+ struct proc_thermal_device *proc_dev;
+
tcc_offset_save = intel_tcc_get_offset(-1);
if (tcc_offset_save < 0)
dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save);
+ proc_dev = dev_get_drvdata(dev);
+
+ if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER)
+ proc_thermal_soc_power_slider_suspend(proc_dev);
+
return 0;
}
EXPORT_SYMBOL_GPL(proc_thermal_suspend);
@@ -356,6 +365,9 @@ int proc_thermal_resume(struct device *dev)
if (tcc_offset_save >= 0)
intel_tcc_set_offset(-1, tcc_offset_save);
+ if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER)
+ proc_thermal_soc_power_slider_resume(proc_dev);
+
return 0;
}
EXPORT_SYMBOL_GPL(proc_thermal_resume);
@@ -399,13 +411,21 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
}
}
+ if (feature_mask & PROC_THERMAL_FEATURE_PTC) {
+ ret = proc_thermal_ptc_add(pdev, proc_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add PTC MMIO interface\n");
+ goto err_rem_rapl;
+ }
+ }
+
if (feature_mask & PROC_THERMAL_FEATURE_FIVR ||
feature_mask & PROC_THERMAL_FEATURE_DVFS ||
feature_mask & PROC_THERMAL_FEATURE_DLVR) {
ret = proc_thermal_rfim_add(pdev, proc_priv);
if (ret) {
dev_err(&pdev->dev, "failed to add RFIM interface\n");
- goto err_rem_rapl;
+ goto err_rem_ptc;
}
}
@@ -423,10 +443,22 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
}
}
+ if (feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) {
+ ret = proc_thermal_soc_power_slider_add(pdev, proc_priv);
+ if (ret) {
+ dev_info(&pdev->dev, "failed to add soc power efficiency slider\n");
+ goto err_rem_wlt;
+ }
+ }
+
return 0;
+err_rem_wlt:
+ proc_thermal_wt_hint_remove(pdev);
err_rem_rfim:
proc_thermal_rfim_remove(pdev);
+err_rem_ptc:
+ proc_thermal_ptc_remove(pdev);
err_rem_rapl:
proc_thermal_rapl_remove();
@@ -439,6 +471,9 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_RAPL)
proc_thermal_rapl_remove();
+ if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC)
+ proc_thermal_ptc_remove(pdev);
+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR ||
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS ||
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR)
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
index ba2d89d3024c..b79937a386ec 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
@@ -27,10 +27,13 @@
#define PCI_DEVICE_ID_INTEL_JSL_THERMAL 0x4E03
#define PCI_DEVICE_ID_INTEL_LNLM_THERMAL 0x641D
#define PCI_DEVICE_ID_INTEL_MTLP_THERMAL 0x7D03
+#define PCI_DEVICE_ID_INTEL_NVL_H_THERMAL 0xD703
+#define PCI_DEVICE_ID_INTEL_NVL_S_THERMAL 0xAD03
#define PCI_DEVICE_ID_INTEL_RPL_THERMAL 0xA71D
#define PCI_DEVICE_ID_INTEL_SKL_THERMAL 0x1903
#define PCI_DEVICE_ID_INTEL_TGL_THERMAL 0x9A03
#define PCI_DEVICE_ID_INTEL_PTL_THERMAL 0xB01D
+#define PCI_DEVICE_ID_INTEL_WCL_THERMAL 0xFD1D
struct power_config {
u32 index;
@@ -67,6 +70,8 @@ struct rapl_mmio_regs {
#define PROC_THERMAL_FEATURE_WT_HINT 0x20
#define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40
#define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80
+#define PROC_THERMAL_FEATURE_PTC 0x100
+#define PROC_THERMAL_FEATURE_SOC_POWER_SLIDER 0x200
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
@@ -123,4 +128,11 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
struct proc_thermal_device *proc_priv,
kernel_ulong_t feature_mask);
void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_ptc_remove(struct pci_dev *pdev);
+
+int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv);
+void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv);
+
#endif
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
index a55aaa8cef42..c693d934103a 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
@@ -485,8 +485,9 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },
{ PCI_DEVICE_DATA(INTEL, LNLM_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |
- PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR |
- PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
+ PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
+ PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR |
+ PROC_THERMAL_FEATURE_PTC) },
{ PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL |
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR |
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
@@ -495,8 +496,25 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, RPL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },
{ PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
- PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_MSI_SUPPORT |
- PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
+ PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
+ PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
+ PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
+ PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
+ { PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |
+ PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR |
+ PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT |
+ PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
+ PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
+ { PCI_DEVICE_DATA(INTEL, NVL_H_THERMAL, PROC_THERMAL_FEATURE_RAPL |
+ PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
+ PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
+ PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
+ PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
+ { PCI_DEVICE_DATA(INTEL, NVL_S_THERMAL, PROC_THERMAL_FEATURE_RAPL |
+ PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
+ PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
+ PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC |
+ PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) },
{ },
};
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c
index bde2cc386afd..f8b9745c1b8a 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c
@@ -11,6 +11,77 @@
static struct rapl_if_priv rapl_mmio_priv;
+/* bitmasks for RAPL MSRs, used by primitive access functions */
+#define MMIO_ENERGY_STATUS_MASK GENMASK(31, 0)
+
+#define MMIO_POWER_LIMIT1_MASK GENMASK(14, 0)
+#define MMIO_POWER_LIMIT1_ENABLE BIT(15)
+#define MMIO_POWER_LIMIT1_CLAMP BIT(16)
+
+#define MMIO_POWER_LIMIT2_MASK GENMASK_ULL(46, 32)
+#define MMIO_POWER_LIMIT2_ENABLE BIT_ULL(47)
+#define MMIO_POWER_LIMIT2_CLAMP BIT_ULL(48)
+
+#define MMIO_POWER_LOW_LOCK BIT(31)
+#define MMIO_POWER_HIGH_LOCK BIT_ULL(63)
+
+#define MMIO_POWER_LIMIT4_MASK GENMASK(12, 0)
+
+#define MMIO_TIME_WINDOW1_MASK GENMASK_ULL(23, 17)
+#define MMIO_TIME_WINDOW2_MASK GENMASK_ULL(55, 49)
+
+#define MMIO_POWER_INFO_MAX_MASK GENMASK_ULL(46, 32)
+#define MMIO_POWER_INFO_MIN_MASK GENMASK_ULL(30, 16)
+#define MMIO_POWER_INFO_MAX_TIME_WIN_MASK GENMASK_ULL(53, 48)
+#define MMIO_POWER_INFO_THERMAL_SPEC_MASK GENMASK(14, 0)
+
+#define MMIO_PERF_STATUS_THROTTLE_TIME_MASK GENMASK(31, 0)
+#define MMIO_PP_POLICY_MASK GENMASK(4, 0)
+
+/* RAPL primitives for MMIO I/F */
+static struct rapl_primitive_info rpi_mmio[NR_RAPL_PRIMITIVES] = {
+ /* name, mask, shift, msr index, unit divisor */
+ [POWER_LIMIT1] = PRIMITIVE_INFO_INIT(POWER_LIMIT1, MMIO_POWER_LIMIT1_MASK, 0,
+ RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
+ [POWER_LIMIT2] = PRIMITIVE_INFO_INIT(POWER_LIMIT2, MMIO_POWER_LIMIT2_MASK, 32,
+ RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
+ [POWER_LIMIT4] = PRIMITIVE_INFO_INIT(POWER_LIMIT4, MMIO_POWER_LIMIT4_MASK, 0,
+ RAPL_DOMAIN_REG_PL4, POWER_UNIT, 0),
+ [ENERGY_COUNTER] = PRIMITIVE_INFO_INIT(ENERGY_COUNTER, MMIO_ENERGY_STATUS_MASK, 0,
+ RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0),
+ [FW_LOCK] = PRIMITIVE_INFO_INIT(FW_LOCK, MMIO_POWER_LOW_LOCK, 31,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ [FW_HIGH_LOCK] = PRIMITIVE_INFO_INIT(FW_LOCK, MMIO_POWER_HIGH_LOCK, 63,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ [PL1_ENABLE] = PRIMITIVE_INFO_INIT(PL1_ENABLE, MMIO_POWER_LIMIT1_ENABLE, 15,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ [PL1_CLAMP] = PRIMITIVE_INFO_INIT(PL1_CLAMP, MMIO_POWER_LIMIT1_CLAMP, 16,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ [PL2_ENABLE] = PRIMITIVE_INFO_INIT(PL2_ENABLE, MMIO_POWER_LIMIT2_ENABLE, 47,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ [PL2_CLAMP] = PRIMITIVE_INFO_INIT(PL2_CLAMP, MMIO_POWER_LIMIT2_CLAMP, 48,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ [TIME_WINDOW1] = PRIMITIVE_INFO_INIT(TIME_WINDOW1, MMIO_TIME_WINDOW1_MASK, 17,
+ RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
+ [TIME_WINDOW2] = PRIMITIVE_INFO_INIT(TIME_WINDOW2, MMIO_TIME_WINDOW2_MASK, 49,
+ RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
+ [THERMAL_SPEC_POWER] = PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER,
+ MMIO_POWER_INFO_THERMAL_SPEC_MASK, 0,
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
+ [MAX_POWER] = PRIMITIVE_INFO_INIT(MAX_POWER, MMIO_POWER_INFO_MAX_MASK, 32,
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
+ [MIN_POWER] = PRIMITIVE_INFO_INIT(MIN_POWER, MMIO_POWER_INFO_MIN_MASK, 16,
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
+ [MAX_TIME_WINDOW] = PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW,
+ MMIO_POWER_INFO_MAX_TIME_WIN_MASK, 48,
+ RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0),
+ [THROTTLED_TIME] = PRIMITIVE_INFO_INIT(THROTTLED_TIME,
+ MMIO_PERF_STATUS_THROTTLE_TIME_MASK, 0,
+ RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0),
+ [PRIORITY_LEVEL] = PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, MMIO_PP_POLICY_MASK, 0,
+ RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0),
+};
+
static const struct rapl_mmio_regs rapl_mmio_default = {
.reg_unit = 0x5938,
.regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0, 0x5930, 0x59b0},
@@ -19,7 +90,14 @@ static const struct rapl_mmio_regs rapl_mmio_default = {
.limits[RAPL_DOMAIN_DRAM] = BIT(POWER_LIMIT2),
};
-static int rapl_mmio_read_raw(int cpu, struct reg_action *ra)
+static const struct rapl_defaults rapl_defaults_mmio = {
+ .floor_freq_reg_addr = 0,
+ .check_unit = rapl_default_check_unit,
+ .set_floor_freq = rapl_default_set_floor_freq,
+ .compute_time_window = rapl_default_compute_time_window,
+};
+
+static int rapl_mmio_read_raw(int cpu, struct reg_action *ra, bool atomic)
{
if (!ra->reg.mmio)
return -EINVAL;
@@ -67,6 +145,8 @@ int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc
rapl_mmio_priv.read_raw = rapl_mmio_read_raw;
rapl_mmio_priv.write_raw = rapl_mmio_write_raw;
+ rapl_mmio_priv.defaults = &rapl_defaults_mmio;
+ rapl_mmio_priv.rpi = rpi_mmio;
rapl_mmio_priv.control_type = powercap_register_control_type(NULL, "intel-rapl-mmio", NULL);
if (IS_ERR(rapl_mmio_priv.control_type)) {
@@ -111,4 +191,5 @@ void proc_thermal_rapl_remove(void)
EXPORT_SYMBOL_GPL(proc_thermal_rapl_remove);
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("INTEL_RAPL");
MODULE_DESCRIPTION("RAPL interface using MMIO");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
index dad63f2d5f90..1a7e134dfcf8 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
@@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/sysfs.h>
#include "processor_thermal_device.h"
MODULE_IMPORT_NS("INT340X_THERMAL");
@@ -87,6 +88,17 @@ static const struct mapping_table lnl_dlvr_mapping[] = {
{NULL, 0, NULL},
};
+static const struct mmio_reg nvl_dlvr_mmio_regs[] = {
+ { 0, 0x19208, 5, 0x1F, 0}, /* dlvr_spread_spectrum_pct */
+ { 0, 0x19208, 1, 0x1, 5}, /* dlvr_control_mode */
+ { 0, 0x19208, 1, 0x1, 6}, /* dlvr_control_lock */
+ { 0, 0x19208, 1, 0x1, 7}, /* dlvr_rfim_enable */
+ { 0, 0x19208, 12, 0xFFF, 8}, /* dlvr_freq_select */
+ { 1, 0x19210, 2, 0x3, 30}, /* dlvr_hardware_rev */
+ { 1, 0x19210, 16, 0xFFFF, 0}, /* dlvr_freq_mhz */
+ { 1, 0x19210, 1, 0x1, 16}, /* dlvr_pll_busy */
+};
+
static int match_mapping_table(const struct mapping_table *table, const char *attr_name,
bool match_int_value, const u32 value, const char *value_str,
char **result_str, u32 *result_int)
@@ -166,15 +178,18 @@ static const struct mmio_reg adl_dvfs_mmio_regs[] = {
{ 0, 0x5A40, 1, 0x1, 0}, /* rfi_disable */
};
+static const struct mapping_table *dlvr_mapping;
+static const struct mmio_reg *dlvr_mmio_regs_table;
+
#define RFIM_SHOW(suffix, table)\
static ssize_t suffix##_show(struct device *dev,\
struct device_attribute *attr,\
char *buf)\
{\
- const struct mapping_table *mapping = NULL;\
+ const struct mmio_reg *mmio_regs = dlvr_mmio_regs_table;\
+ const struct mapping_table *mapping = dlvr_mapping;\
struct proc_thermal_device *proc_priv;\
struct pci_dev *pdev = to_pci_dev(dev);\
- const struct mmio_reg *mmio_regs;\
const char **match_strs;\
int ret, err;\
u32 reg_val;\
@@ -186,12 +201,6 @@ static ssize_t suffix##_show(struct device *dev,\
mmio_regs = adl_dvfs_mmio_regs;\
} else if (table == 2) { \
match_strs = (const char **)dlvr_strings;\
- if (pdev->device == PCI_DEVICE_ID_INTEL_LNLM_THERMAL) {\
- mmio_regs = lnl_dlvr_mmio_regs;\
- mapping = lnl_dlvr_mapping;\
- } else {\
- mmio_regs = dlvr_mmio_regs;\
- } \
} else {\
match_strs = (const char **)fivr_strings;\
mmio_regs = tgl_fivr_mmio_regs;\
@@ -203,9 +212,9 @@ static ssize_t suffix##_show(struct device *dev,\
ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;\
err = get_mapped_string(mapping, attr->attr.name, ret, &str);\
if (!err)\
- return sprintf(buf, "%s\n", str);\
+ return sysfs_emit(buf, "%s\n", str);\
if (err == -EOPNOTSUPP)\
- return sprintf(buf, "%u\n", ret);\
+ return sysfs_emit(buf, "%u\n", ret);\
return err;\
}
@@ -214,12 +223,12 @@ static ssize_t suffix##_store(struct device *dev,\
struct device_attribute *attr,\
const char *buf, size_t count)\
{\
- const struct mapping_table *mapping = NULL;\
+ const struct mmio_reg *mmio_regs = dlvr_mmio_regs_table;\
+ const struct mapping_table *mapping = dlvr_mapping;\
struct proc_thermal_device *proc_priv;\
struct pci_dev *pdev = to_pci_dev(dev);\
unsigned int input;\
const char **match_strs;\
- const struct mmio_reg *mmio_regs;\
int ret, err;\
u32 reg_val;\
u32 mask;\
@@ -230,12 +239,6 @@ static ssize_t suffix##_store(struct device *dev,\
mmio_regs = adl_dvfs_mmio_regs;\
} else if (table == 2) { \
match_strs = (const char **)dlvr_strings;\
- if (pdev->device == PCI_DEVICE_ID_INTEL_LNLM_THERMAL) {\
- mmio_regs = lnl_dlvr_mmio_regs;\
- mapping = lnl_dlvr_mapping;\
- } else {\
- mmio_regs = dlvr_mmio_regs;\
- } \
} else {\
match_strs = (const char **)fivr_strings;\
mmio_regs = tgl_fivr_mmio_regs;\
@@ -396,22 +399,36 @@ static ssize_t rfi_restriction_show(struct device *dev,
if (ret)
return ret;
- return sprintf(buf, "%llu\n", resp);
+ return sysfs_emit(buf, "%llu\n", resp);
}
+ /* ddr_data_rate */
+static const struct mmio_reg nvl_ddr_data_rate_reg = { 1, 0xE0, 10, 0x3FF, 2};
+
+static const struct mmio_reg *ddr_data_rate_reg;
+
static ssize_t ddr_data_rate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- u16 id = 0x0107;
u64 resp;
- int ret;
- ret = processor_thermal_send_mbox_read_cmd(to_pci_dev(dev), id, &resp);
- if (ret)
- return ret;
+ if (ddr_data_rate_reg) {
+ u16 reg_val;
- return sprintf(buf, "%llu\n", resp);
+ pci_read_config_word(to_pci_dev(dev), ddr_data_rate_reg->offset, &reg_val);
+ resp = (reg_val >> ddr_data_rate_reg->shift) & ddr_data_rate_reg->mask;
+ resp = (resp * 3333) / 100;
+ } else {
+ const u16 id = 0x0107;
+ int ret;
+
+ ret = processor_thermal_send_mbox_read_cmd(to_pci_dev(dev), id, &resp);
+ if (ret)
+ return ret;
+ }
+
+ return sysfs_emit(buf, "%llu\n", resp);
}
static DEVICE_ATTR_RW(rfi_restriction);
@@ -448,9 +465,28 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc
}
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) {
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_LNLM_THERMAL:
+ case PCI_DEVICE_ID_INTEL_PTL_THERMAL:
+ case PCI_DEVICE_ID_INTEL_WCL_THERMAL:
+ dlvr_mmio_regs_table = lnl_dlvr_mmio_regs;
+ dlvr_mapping = lnl_dlvr_mapping;
+ break;
+ case PCI_DEVICE_ID_INTEL_NVL_H_THERMAL:
+ case PCI_DEVICE_ID_INTEL_NVL_S_THERMAL:
+ dlvr_mmio_regs_table = nvl_dlvr_mmio_regs;
+ ddr_data_rate_reg = &nvl_ddr_data_rate_reg;
+ break;
+ default:
+ dlvr_mmio_regs_table = dlvr_mmio_regs;
+ break;
+ }
ret = sysfs_create_group(&pdev->dev.kobj, &dlvr_attribute_group);
- if (ret)
+ if (ret) {
+ if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
+ sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
return ret;
+ }
}
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c
new file mode 100644
index 000000000000..91f291627132
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Processor Thermal Device Interface for Reading and Writing
+ * SoC Power Slider Values from User Space.
+ *
+ * Operation:
+ * The SOC_EFFICIENCY_SLIDER_0_0_0_MCHBAR register is accessed
+ * using the MMIO (Memory-Mapped I/O) interface with an MMIO offset of 0x5B38.
+ * Although this register is 64 bits wide, only bits 7:0 are used,
+ * and the other bits remain unchanged.
+ *
+ * Bit definitions
+ *
+ * Bits 2:0 (Slider value):
+ * The SoC optimizer slider value indicates the system wide energy performance
+ * hint. The slider has no specific units and ranges from 0 (highest
+ * performance) to 6 (highest energy efficiency). Value of 7 is reserved.
+ * Bits 3 : Reserved
+ * Bits 6:4 (Offset)
+ * Offset allows the SoC to automatically switch slider position in range
+ * [slider value (bits 2:0) + offset] to improve power efficiency based on
+ * internal SoC algorithms.
+ * Bit 7 (Enable):
+ * If this bit is set, the SoC Optimization sliders will be processed by the
+ * SoC firmware.
+ *
+ * Copyright (c) 2025, Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/pci.h>
+#include <linux/platform_profile.h>
+#include "processor_thermal_device.h"
+
+#define SOC_POWER_SLIDER_OFFSET 0x5B38
+
+enum power_slider_preference {
+ SOC_POWER_SLIDER_PERFORMANCE,
+ SOC_POWER_SLIDER_BALANCE,
+ SOC_POWER_SLIDER_POWERSAVE,
+};
+
+#define SOC_SLIDER_VALUE_MINIMUM 0x00
+#define SOC_SLIDER_VALUE_BALANCE 0x03
+#define SOC_SLIDER_VALUE_MAXIMUM 0x06
+
+#define SLIDER_MASK GENMASK_ULL(2, 0)
+#define SLIDER_ENABLE_BIT 7
+
+static u8 slider_values[] = {
+ [SOC_POWER_SLIDER_PERFORMANCE] = SOC_SLIDER_VALUE_MINIMUM,
+ [SOC_POWER_SLIDER_BALANCE] = SOC_SLIDER_VALUE_BALANCE,
+ [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM,
+};
+
+/* Lock to protect module param updates */
+static DEFINE_MUTEX(slider_param_lock);
+
+static int slider_balanced_param = SOC_SLIDER_VALUE_BALANCE;
+
+static int slider_def_balance_set(const char *arg, const struct kernel_param *kp)
+{
+ u8 slider_val;
+ int ret;
+
+ guard(mutex)(&slider_param_lock);
+
+ ret = kstrtou8(arg, 16, &slider_val);
+ if (!ret) {
+ if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] ||
+ slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE])
+ return -EINVAL;
+
+ slider_balanced_param = slider_val;
+ }
+
+ return ret;
+}
+
+static int slider_def_balance_get(char *buf, const struct kernel_param *kp)
+{
+ guard(mutex)(&slider_param_lock);
+ return sysfs_emit(buf, "%02x\n", slider_values[SOC_POWER_SLIDER_BALANCE]);
+}
+
+static const struct kernel_param_ops slider_def_balance_ops = {
+ .set = slider_def_balance_set,
+ .get = slider_def_balance_get,
+};
+
+module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644);
+MODULE_PARM_DESC(slider_balance, "Set slider default value for balance");
+
+static u8 slider_offset;
+
+static int slider_def_offset_set(const char *arg, const struct kernel_param *kp)
+{
+ u8 offset;
+ int ret;
+
+ guard(mutex)(&slider_param_lock);
+
+ ret = kstrtou8(arg, 16, &offset);
+ if (!ret) {
+ if (offset > SOC_SLIDER_VALUE_MAXIMUM)
+ return -EINVAL;
+
+ slider_offset = offset;
+ }
+
+ return ret;
+}
+
+static int slider_def_offset_get(char *buf, const struct kernel_param *kp)
+{
+ guard(mutex)(&slider_param_lock);
+ return sysfs_emit(buf, "%02x\n", slider_offset);
+}
+
+static const struct kernel_param_ops slider_offset_ops = {
+ .set = slider_def_offset_set,
+ .get = slider_def_offset_get,
+};
+
+/*
+ * To enhance power efficiency dynamically, the firmware can optionally
+ * auto-adjust the slider value based on the current workload. This
+ * adjustment is controlled by the "slider_offset" module parameter.
+ * This offset permits the firmware to increase the slider value
+ * up to and including "SoC slider + slider offset,".
+ */
+module_param_cb(slider_offset, &slider_offset_ops, NULL, 0644);
+MODULE_PARM_DESC(slider_offset, "Set slider offset");
+
+/* Convert from platform power profile option to SoC slider value */
+static int convert_profile_to_power_slider(enum platform_profile_option profile)
+{
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ return slider_values[SOC_POWER_SLIDER_POWERSAVE];
+ case PLATFORM_PROFILE_BALANCED:
+ return slider_values[SOC_POWER_SLIDER_BALANCE];
+ case PLATFORM_PROFILE_PERFORMANCE:
+ return slider_values[SOC_POWER_SLIDER_PERFORMANCE];
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* Convert to platform power profile option from SoC slider values */
+static int convert_power_slider_to_profile(u8 slider)
+{
+ if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE])
+ return PLATFORM_PROFILE_PERFORMANCE;
+ if (slider == slider_values[SOC_POWER_SLIDER_BALANCE])
+ return PLATFORM_PROFILE_BALANCED;
+ if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE])
+ return PLATFORM_PROFILE_LOW_POWER;
+
+ return -EOPNOTSUPP;
+}
+
+static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv)
+{
+ return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET);
+}
+
+static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val)
+{
+ writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET);
+}
+
+#define SLIDER_OFFSET_MASK GENMASK_ULL(6, 4)
+
+static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider)
+{
+ u8 offset;
+ u64 val;
+
+ val = read_soc_slider(proc_priv);
+ val &= ~SLIDER_MASK;
+ val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT);
+
+ if (slider == SOC_SLIDER_VALUE_MINIMUM || slider == SOC_SLIDER_VALUE_MAXIMUM)
+ offset = 0;
+ else
+ offset = slider_offset;
+
+ /* Set the slider offset from module params */
+ val &= ~SLIDER_OFFSET_MASK;
+ val |= FIELD_PREP(SLIDER_OFFSET_MASK, offset);
+
+ write_soc_slider(proc_priv, val);
+}
+
+/* profile get/set callbacks are called with a profile lock, so no need for local locks */
+
+static int power_slider_platform_profile_set(struct device *dev,
+ enum platform_profile_option profile)
+{
+ struct proc_thermal_device *proc_priv;
+ int slider;
+
+ proc_priv = dev_get_drvdata(dev);
+ if (!proc_priv)
+ return -EOPNOTSUPP;
+
+ guard(mutex)(&slider_param_lock);
+
+ slider_values[SOC_POWER_SLIDER_BALANCE] = slider_balanced_param;
+
+ slider = convert_profile_to_power_slider(profile);
+ if (slider < 0)
+ return slider;
+
+ set_soc_power_profile(proc_priv, slider);
+
+ return 0;
+}
+
+static int power_slider_platform_profile_get(struct device *dev,
+ enum platform_profile_option *profile)
+{
+ struct proc_thermal_device *proc_priv;
+ int slider, ret;
+ u64 val;
+
+ proc_priv = dev_get_drvdata(dev);
+ if (!proc_priv)
+ return -EOPNOTSUPP;
+
+ val = read_soc_slider(proc_priv);
+ slider = FIELD_GET(SLIDER_MASK, val);
+
+ ret = convert_power_slider_to_profile(slider);
+ if (ret < 0)
+ return ret;
+
+ *profile = ret;
+
+ return 0;
+}
+
+static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops power_slider_platform_profile_ops = {
+ .probe = power_slider_platform_profile_probe,
+ .profile_get = power_slider_platform_profile_get,
+ .profile_set = power_slider_platform_profile_set,
+};
+
+int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+ struct device *ppdev;
+
+ set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]);
+
+ ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv,
+ &power_slider_platform_profile_ops);
+
+ return PTR_ERR_OR_ZERO(ppdev);
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL");
+
+static u64 soc_slider_save;
+
+void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv)
+{
+ soc_slider_save = read_soc_slider(proc_priv);
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL");
+
+void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv)
+{
+ write_soc_slider(proc_priv, soc_slider_save);
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL");
+
+MODULE_IMPORT_NS("INT340X_THERMAL");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Processor Thermal Power Slider Interface");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
index 68e8391af8f4..f8c33e8e5b7a 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
@@ -34,6 +34,7 @@
#define SOC_WT GENMASK_ULL(47, 40)
+#define SOC_WT_SLOW_PREDICTION_INT_ENABLE_BIT 22
#define SOC_WT_PREDICTION_INT_ENABLE_BIT 23
#define SOC_WT_PREDICTION_INT_ACTIVE BIT(2)
@@ -47,6 +48,7 @@ static u16 notify_delay_ms = 1024;
static DEFINE_MUTEX(wt_lock);
static u8 wt_enable;
+static u8 wt_slow_enable;
/* Show current predicted workload type index */
static ssize_t workload_type_index_show(struct device *dev,
@@ -59,7 +61,7 @@ static ssize_t workload_type_index_show(struct device *dev,
int wt;
mutex_lock(&wt_lock);
- if (!wt_enable) {
+ if (!wt_enable && !wt_slow_enable) {
mutex_unlock(&wt_lock);
return -ENODATA;
}
@@ -84,9 +86,9 @@ static ssize_t workload_hint_enable_show(struct device *dev,
return sysfs_emit(buf, "%d\n", wt_enable);
}
-static ssize_t workload_hint_enable_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
+static ssize_t workload_hint_enable(struct device *dev, u8 enable_bit, u8 *status,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
{
struct pci_dev *pdev = to_pci_dev(dev);
u8 mode;
@@ -99,17 +101,17 @@ static ssize_t workload_hint_enable_store(struct device *dev,
if (mode)
ret = processor_thermal_mbox_interrupt_config(pdev, true,
- SOC_WT_PREDICTION_INT_ENABLE_BIT,
+ enable_bit,
notify_delay);
else
ret = processor_thermal_mbox_interrupt_config(pdev, false,
- SOC_WT_PREDICTION_INT_ENABLE_BIT, 0);
+ enable_bit, 0);
if (ret)
goto ret_enable_store;
ret = size;
- wt_enable = mode;
+ *status = mode;
ret_enable_store:
mutex_unlock(&wt_lock);
@@ -117,8 +119,28 @@ ret_enable_store:
return ret;
}
+static ssize_t workload_hint_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ return workload_hint_enable(dev, SOC_WT_PREDICTION_INT_ENABLE_BIT, &wt_enable,
+ attr, buf, size);
+}
static DEVICE_ATTR_RW(workload_hint_enable);
+static ssize_t workload_slow_hint_enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%d\n", wt_slow_enable);
+}
+
+static ssize_t workload_slow_hint_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ return workload_hint_enable(dev, SOC_WT_SLOW_PREDICTION_INT_ENABLE_BIT, &wt_slow_enable,
+ attr, buf, size);
+}
+static DEVICE_ATTR_RW(workload_slow_hint_enable);
+
static ssize_t notification_delay_ms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -178,16 +200,35 @@ static ssize_t notification_delay_ms_store(struct device *dev,
static DEVICE_ATTR_RW(notification_delay_ms);
+static umode_t workload_hint_attr_visible(struct kobject *kobj, struct attribute *attr, int unused)
+{
+ if (attr != &dev_attr_workload_slow_hint_enable.attr)
+ return attr->mode;
+
+ switch (to_pci_dev(kobj_to_dev(kobj))->device) {
+ case PCI_DEVICE_ID_INTEL_LNLM_THERMAL:
+ case PCI_DEVICE_ID_INTEL_MTLP_THERMAL:
+ case PCI_DEVICE_ID_INTEL_ARL_S_THERMAL:
+ return 0;
+ default:
+ break;
+ }
+
+ return attr->mode;
+}
+
static struct attribute *workload_hint_attrs[] = {
&dev_attr_workload_type_index.attr,
&dev_attr_workload_hint_enable.attr,
+ &dev_attr_workload_slow_hint_enable.attr,
&dev_attr_notification_delay_ms.attr,
NULL
};
static const struct attribute_group workload_hint_attribute_group = {
.attrs = workload_hint_attrs,
- .name = "workload_hint"
+ .name = "workload_hint",
+ .is_visible = workload_hint_attr_visible
};
/*
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c
index b95810f4a011..2372f5202019 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c
@@ -7,6 +7,7 @@
*/
#include <linux/pci.h>
+#include <linux/sysfs.h>
#include "processor_thermal_device.h"
/* List of workload types */
@@ -28,9 +29,9 @@ static ssize_t workload_available_types_show(struct device *dev,
int ret = 0;
while (workload_types[i] != NULL)
- ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
+ ret += sysfs_emit_at(buf, ret, "%s ", workload_types[i++]);
- ret += sprintf(&buf[ret], "\n");
+ ret += sysfs_emit_at(buf, ret, "\n");
return ret;
}
@@ -85,7 +86,7 @@ static ssize_t workload_type_show(struct device *dev,
if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
return -EINVAL;
- return sprintf(buf, "%s\n", workload_types[cmd_resp]);
+ return sysfs_emit(buf, "%s\n", workload_types[cmd_resp]);
}
static DEVICE_ATTR_RW(workload_type);
diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c
index 5b18a46a10b0..8c4ae75231f8 100644
--- a/drivers/thermal/intel/intel_hfi.c
+++ b/drivers/thermal/intel/intel_hfi.c
@@ -212,7 +212,7 @@ static void update_capabilities(struct hfi_instance *hfi_instance)
if (!cpu_count)
goto out;
- cpu_caps = kcalloc(cpu_count, sizeof(*cpu_caps), GFP_KERNEL);
+ cpu_caps = kzalloc_objs(*cpu_caps, cpu_count);
if (!cpu_caps)
goto out;
@@ -284,7 +284,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
if (!raw_spin_trylock(&hfi_instance->event_lock))
return;
- rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr);
+ rdmsrq(MSR_IA32_PACKAGE_THERM_STATUS, msr);
hfi = msr & PACKAGE_THERM_STATUS_HFI_UPDATED;
if (!hfi) {
raw_spin_unlock(&hfi_instance->event_lock);
@@ -356,9 +356,9 @@ static void hfi_enable(void)
{
u64 msr_val;
- rdmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
+ rdmsrq(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
msr_val |= HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT;
- wrmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
+ wrmsrq(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
}
static void hfi_set_hw_table(struct hfi_instance *hfi_instance)
@@ -368,7 +368,7 @@ static void hfi_set_hw_table(struct hfi_instance *hfi_instance)
hw_table_pa = virt_to_phys(hfi_instance->hw_table);
msr_val = hw_table_pa | HW_FEEDBACK_PTR_VALID_BIT;
- wrmsrl(MSR_IA32_HW_FEEDBACK_PTR, msr_val);
+ wrmsrq(MSR_IA32_HW_FEEDBACK_PTR, msr_val);
}
/* Caller must hold hfi_instance_lock. */
@@ -377,9 +377,9 @@ static void hfi_disable(void)
u64 msr_val;
int i;
- rdmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
+ rdmsrq(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
msr_val &= ~HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT;
- wrmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
+ wrmsrq(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
/*
* Wait for hardware to acknowledge the disabling of HFI. Some
@@ -388,7 +388,7 @@ static void hfi_disable(void)
* memory.
*/
for (i = 0; i < 2000; i++) {
- rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
+ rdmsrq(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
if (msr_val & PACKAGE_THERM_STATUS_HFI_UPDATED)
break;
@@ -526,7 +526,7 @@ void intel_hfi_offline(unsigned int cpu)
mutex_lock(&hfi_instance_lock);
cpumask_clear_cpu(cpu, hfi_instance->cpus);
- if (!cpumask_weight(hfi_instance->cpus))
+ if (cpumask_empty(hfi_instance->cpus))
hfi_disable();
mutex_unlock(&hfi_instance_lock);
@@ -592,7 +592,7 @@ static void hfi_disable_instance(void *ptr)
hfi_disable();
}
-static void hfi_syscore_resume(void)
+static void hfi_syscore_resume(void *data)
{
/* This code runs only on the boot CPU. */
struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0);
@@ -603,7 +603,7 @@ static void hfi_syscore_resume(void)
hfi_enable_instance(hfi_instance);
}
-static int hfi_syscore_suspend(void)
+static int hfi_syscore_suspend(void *data)
{
/* No locking needed. There is no concurrency with CPU offline. */
hfi_disable();
@@ -611,11 +611,15 @@ static int hfi_syscore_suspend(void)
return 0;
}
-static struct syscore_ops hfi_pm_ops = {
+static const struct syscore_ops hfi_pm_ops = {
.resume = hfi_syscore_resume,
.suspend = hfi_syscore_suspend,
};
+static struct syscore hfi_pm = {
+ .ops = &hfi_pm_ops,
+};
+
static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state,
void *_notify)
{
@@ -686,8 +690,7 @@ void __init intel_hfi_init(void)
* This allocation may fail. CPU hotplug callbacks must check
* for a null pointer.
*/
- hfi_instances = kcalloc(max_hfi_instances, sizeof(*hfi_instances),
- GFP_KERNEL);
+ hfi_instances = kzalloc_objs(*hfi_instances, max_hfi_instances);
if (!hfi_instances)
return;
@@ -710,7 +713,7 @@ void __init intel_hfi_init(void)
if (thermal_genl_register_notifier(&hfi_thermal_nb))
goto err_nl_notif;
- register_syscore_ops(&hfi_pm_ops);
+ register_syscore(&hfi_pm);
return;
diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c
index fc326985796c..52e71af67dc6 100644
--- a/drivers/thermal/intel/intel_pch_thermal.c
+++ b/drivers/thermal/intel/intel_pch_thermal.c
@@ -269,7 +269,6 @@ static void intel_pch_thermal_remove(struct pci_dev *pdev)
thermal_zone_device_unregister(ptd->tzd);
iounmap(ptd->hw_base);
- pci_set_drvdata(pdev, NULL);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c
index 96a24df79686..ccf380da12f2 100644
--- a/drivers/thermal/intel/intel_powerclamp.c
+++ b/drivers/thermal/intel/intel_powerclamp.c
@@ -200,8 +200,7 @@ static int cpumask_get(char *buf, const struct kernel_param *kp)
if (!cpumask_available(idle_injection_cpu_mask))
return -ENODEV;
- return bitmap_print_to_pagebuf(false, buf, cpumask_bits(idle_injection_cpu_mask),
- nr_cpumask_bits);
+ return cpumap_print_to_pagebuf(false, buf, idle_injection_cpu_mask);
}
static const struct kernel_param_ops cpumask_ops = {
@@ -340,7 +339,7 @@ static bool has_pkg_state_counter(void)
/* check if any one of the counter msrs exists */
while (info->msr_index) {
- if (!rdmsrl_safe(info->msr_index, &val))
+ if (!rdmsrq_safe(info->msr_index, &val))
return true;
info++;
}
@@ -356,7 +355,7 @@ static u64 pkg_state_counter(void)
while (info->msr_index) {
if (!info->skip) {
- if (!rdmsrl_safe(info->msr_index, &val))
+ if (!rdmsrq_safe(info->msr_index, &val))
count += val;
else
info->skip = true;
diff --git a/drivers/thermal/intel/intel_quark_dts_thermal.c b/drivers/thermal/intel/intel_quark_dts_thermal.c
index 89498eb29a89..15476699bdda 100644
--- a/drivers/thermal/intel/intel_quark_dts_thermal.c
+++ b/drivers/thermal/intel/intel_quark_dts_thermal.c
@@ -336,7 +336,7 @@ static struct soc_sensor_entry *alloc_soc_dts(void)
int err;
u32 out;
- aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
+ aux_entry = kzalloc_obj(*aux_entry);
if (!aux_entry) {
err = -ENOMEM;
return ERR_PTR(-ENOMEM);
diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c
index ea87439fe7a9..93773878398e 100644
--- a/drivers/thermal/intel/intel_soc_dts_iosf.c
+++ b/drivers/thermal/intel/intel_soc_dts_iosf.c
@@ -320,7 +320,7 @@ intel_soc_dts_iosf_init(enum intel_soc_dts_interrupt_type intr_type,
if (tj_max < 0)
return ERR_PTR(tj_max);
- sensors = kzalloc(sizeof(*sensors), GFP_KERNEL);
+ sensors = kzalloc_obj(*sensors);
if (!sensors)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/thermal/intel/intel_tcc.c b/drivers/thermal/intel/intel_tcc.c
index 817421508d5c..ab61fb122937 100644
--- a/drivers/thermal/intel/intel_tcc.c
+++ b/drivers/thermal/intel/intel_tcc.c
@@ -106,7 +106,7 @@ static const struct x86_cpu_id intel_tcc_cpu_ids[] __initconst = {
X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_D, &temp_broadwell),
X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID, &temp_broadwell),
X86_MATCH_VFM(INTEL_ATOM_AIRMONT, &temp_broadwell),
- X86_MATCH_VFM(INTEL_ATOM_AIRMONT_MID, &temp_broadwell),
+ X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID2, &temp_broadwell),
X86_MATCH_VFM(INTEL_ATOM_AIRMONT_NP, &temp_broadwell),
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, &temp_goldmont),
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_D, &temp_goldmont),
@@ -172,7 +172,7 @@ static u32 get_temp_mask(bool pkg)
/**
* intel_tcc_get_tjmax() - returns the default TCC activation Temperature
- * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ * @cpu: cpu that the MSR should be run on, negative value means any cpu.
*
* Get the TjMax value, which is the default thermal throttling or TCC
* activation temperature in degrees C.
@@ -199,7 +199,7 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_get_tjmax, "INTEL_TCC");
/**
* intel_tcc_get_offset() - returns the TCC Offset value to Tjmax
- * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ * @cpu: cpu that the MSR should be run on, negative value means any cpu.
*
* Get the TCC offset value to Tjmax. The effective thermal throttling or TCC
* activation temperature equals "Tjmax" - "TCC Offset", in degrees C.
@@ -224,7 +224,7 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, "INTEL_TCC");
/**
* intel_tcc_set_offset() - set the TCC offset value to Tjmax
- * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ * @cpu: cpu that the MSR should be run on, negative value means any cpu.
* @offset: TCC offset value in degree C
*
* Set the TCC Offset value to Tjmax. The effective thermal throttling or TCC
@@ -267,7 +267,7 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, "INTEL_TCC");
/**
* intel_tcc_get_temp() - returns the current temperature
- * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ * @cpu: cpu that the MSR should be run on, negative value means any cpu.
* @temp: pointer to the memory for saving cpu temperature.
* @pkg: true: Package Thermal Sensor. false: Core Thermal Sensor.
*
diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c
index 9ff0ebdde0ef..6c2ba0ba3178 100644
--- a/drivers/thermal/intel/intel_tcc_cooling.c
+++ b/drivers/thermal/intel/intel_tcc_cooling.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/thermal.h>
#include <asm/cpu_device_id.h>
+#include <asm/msr.h>
#define TCC_PROGRAMMABLE BIT(30)
#define TCC_LOCKED BIT(31)
@@ -64,6 +65,10 @@ static const struct x86_cpu_id tcc_ids[] __initconst = {
X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL),
X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL),
X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL),
+ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL),
+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL),
+ X86_MATCH_VFM(INTEL_NOVALAKE, NULL),
+ X86_MATCH_VFM(INTEL_NOVALAKE_L, NULL),
{}
};
@@ -71,24 +76,22 @@ MODULE_DEVICE_TABLE(x86cpu, tcc_ids);
static int __init tcc_cooling_init(void)
{
- int ret;
u64 val;
const struct x86_cpu_id *id;
-
int err;
id = x86_match_cpu(tcc_ids);
if (!id)
return -ENODEV;
- err = rdmsrl_safe(MSR_PLATFORM_INFO, &val);
+ err = rdmsrq_safe(MSR_PLATFORM_INFO, &val);
if (err)
return err;
if (!(val & TCC_PROGRAMMABLE))
return -ENODEV;
- err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
+ err = rdmsrq_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
@@ -102,10 +105,9 @@ static int __init tcc_cooling_init(void)
tcc_cdev =
thermal_cooling_device_register("TCC Offset", NULL,
&tcc_cooling_ops);
- if (IS_ERR(tcc_cdev)) {
- ret = PTR_ERR(tcc_cdev);
- return ret;
- }
+ if (IS_ERR(tcc_cdev))
+ return PTR_ERR(tcc_cdev);
+
return 0;
}
diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c
index e69868e868eb..44fa4dd15dd1 100644
--- a/drivers/thermal/intel/therm_throt.c
+++ b/drivers/thermal/intel/therm_throt.c
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/smp.h>
+#include <linux/sysfs.h>
#include <linux/cpu.h>
#include <asm/processor.h>
@@ -144,8 +145,8 @@ static ssize_t therm_throt_device_show_##event##_##name( \
\
preempt_disable(); /* CPU hotplug */ \
if (cpu_online(cpu)) { \
- ret = sprintf(buf, "%lu\n", \
- per_cpu(thermal_state, cpu).event.name); \
+ ret = sysfs_emit(buf, "%lu\n", \
+ per_cpu(thermal_state, cpu).event.name); \
} else \
ret = 0; \
preempt_enable(); \
@@ -273,7 +274,7 @@ void thermal_clear_package_intr_status(int level, u64 bit_mask)
}
msr_val &= ~bit_mask;
- wrmsrl(msr, msr_val);
+ wrmsrq(msr, msr_val);
}
EXPORT_SYMBOL_GPL(thermal_clear_package_intr_status);
@@ -287,7 +288,7 @@ static void get_therm_status(int level, bool *proc_hot, u8 *temp)
else
msr = MSR_IA32_PACKAGE_THERM_STATUS;
- rdmsrl(msr, msr_val);
+ rdmsrq(msr, msr_val);
if (msr_val & THERM_STATUS_PROCHOT_LOG)
*proc_hot = true;
else
@@ -643,7 +644,7 @@ static void notify_thresholds(__u64 msr_val)
void __weak notify_hwp_interrupt(void)
{
- wrmsrl_safe(MSR_HWP_STATUS, 0);
+ wrmsrq_safe(MSR_HWP_STATUS, 0);
}
/* Thermal transition interrupt handler */
@@ -654,7 +655,7 @@ void intel_thermal_interrupt(void)
if (static_cpu_has(X86_FEATURE_HWP))
notify_hwp_interrupt();
- rdmsrl(MSR_IA32_THERM_STATUS, msr_val);
+ rdmsrq(MSR_IA32_THERM_STATUS, msr_val);
/* Check for violation of core thermal thresholds*/
notify_thresholds(msr_val);
@@ -669,7 +670,7 @@ void intel_thermal_interrupt(void)
CORE_LEVEL);
if (this_cpu_has(X86_FEATURE_PTS)) {
- rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
+ rdmsrq(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
/* check violations of package thermal thresholds */
notify_package_thresholds(msr_val);
therm_throt_process(msr_val & PACKAGE_THERM_STATUS_PROCHOT,
diff --git a/drivers/thermal/intel/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c
index 496abf8e55e0..540109761f0a 100644
--- a/drivers/thermal/intel/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c
@@ -20,6 +20,7 @@
#include <linux/debugfs.h>
#include <asm/cpu_device_id.h>
+#include <asm/msr.h>
#include "thermal_interrupt.h"
@@ -127,6 +128,9 @@ sys_set_trip_temp(struct thermal_zone_device *tzd,
u32 l, h, mask, shift, intr;
int tj_max, val, ret;
+ if (temp == THERMAL_TEMP_INVALID)
+ temp = 0;
+
tj_max = intel_tcc_get_tjmax(zonedev->cpu);
if (tj_max < 0)
return tj_max;
@@ -329,8 +333,9 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
tj_max = intel_tcc_get_tjmax(cpu);
if (tj_max < 0)
return tj_max;
+ tj_max *= 1000;
- zonedev = kzalloc(sizeof(*zonedev), GFP_KERNEL);
+ zonedev = kzalloc_obj(*zonedev);
if (!zonedev)
return -ENOMEM;
@@ -487,8 +492,7 @@ static int __init pkg_temp_thermal_init(void)
return -ENODEV;
max_id = topology_max_packages() * topology_max_dies_per_package();
- zones = kcalloc(max_id, sizeof(struct zone_device *),
- GFP_KERNEL);
+ zones = kzalloc_objs(struct zone_device *, max_id);
if (!zones)
return -ENOMEM;
diff --git a/drivers/thermal/k3_j72xx_bandgap.c b/drivers/thermal/k3_j72xx_bandgap.c
index 70de6dbf99c5..6aba00d8b8fd 100644
--- a/drivers/thermal/k3_j72xx_bandgap.c
+++ b/drivers/thermal/k3_j72xx_bandgap.c
@@ -20,6 +20,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
+#include "thermal_hwmon.h"
+
#define K3_VTM_DEVINFO_PWR0_OFFSET 0x4
#define K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK 0xf0
#define K3_VTM_TMPSENS0_CTRL_OFFSET 0x300
@@ -460,13 +462,13 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev)
goto err_alloc;
}
- ref_table = kzalloc(sizeof(*ref_table) * TABLE_SIZE, GFP_KERNEL);
+ ref_table = kzalloc_objs(*ref_table, TABLE_SIZE);
if (!ref_table) {
ret = -ENOMEM;
goto err_alloc;
}
- derived_table = devm_kzalloc(bgp->dev, sizeof(*derived_table) * TABLE_SIZE,
+ derived_table = devm_kcalloc(bgp->dev, TABLE_SIZE, sizeof(*derived_table),
GFP_KERNEL);
if (!derived_table) {
ret = -ENOMEM;
@@ -513,6 +515,8 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev)
ret = PTR_ERR(ti_thermal);
goto err_free_ref_table;
}
+
+ devm_thermal_add_hwmon_sysfs(bgp->dev, ti_thermal);
}
platform_set_drvdata(pdev, bgp);
diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c
index 7c2265231668..4619e090f756 100644
--- a/drivers/thermal/kirkwood_thermal.c
+++ b/drivers/thermal/kirkwood_thermal.c
@@ -48,7 +48,7 @@ static int kirkwood_get_temp(struct thermal_zone_device *thermal,
return 0;
}
-static struct thermal_zone_device_ops ops = {
+static const struct thermal_zone_device_ops ops = {
.get_temp = kirkwood_get_temp,
};
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 2d6b75b0539f..ea4dd2fb1f47 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -112,13 +112,19 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low,
return loongson2_thermal_set(data, low/MILLI, high/MILLI, true);
}
-static struct thermal_zone_device_ops loongson2_of_thermal_ops = {
+static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = {
.get_temp = loongson2_2k1000_get_temp,
.set_trips = loongson2_thermal_set_trips,
};
+static const struct thermal_zone_device_ops loongson2_2k2000_of_thermal_ops = {
+ .get_temp = loongson2_2k2000_get_temp,
+ .set_trips = loongson2_thermal_set_trips,
+};
+
static int loongson2_thermal_probe(struct platform_device *pdev)
{
+ const struct thermal_zone_device_ops *thermal_ops;
struct device *dev = &pdev->dev;
struct loongson2_thermal_data *data;
struct thermal_zone_device *tzd;
@@ -140,7 +146,9 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
if (IS_ERR(data->temp_reg))
return PTR_ERR(data->temp_reg);
- loongson2_of_thermal_ops.get_temp = loongson2_2k2000_get_temp;
+ thermal_ops = &loongson2_2k2000_of_thermal_ops;
+ } else {
+ thermal_ops = &loongson2_2k1000_of_thermal_ops;
}
irq = platform_get_irq(pdev, 0);
@@ -152,8 +160,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
loongson2_thermal_set(data, 0, 0, false);
for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) {
- tzd = devm_thermal_of_zone_register(dev, i, data,
- &loongson2_of_thermal_ops);
+ tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops);
if (!IS_ERR(tzd))
break;
diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c
index 07f7f3b7a2fb..a9617d5e0077 100644
--- a/drivers/thermal/mediatek/lvts_thermal.c
+++ b/drivers/thermal/mediatek/lvts_thermal.c
@@ -44,6 +44,11 @@
#define LVTS_EDATA01(__base) (__base + 0x0058)
#define LVTS_EDATA02(__base) (__base + 0x005C)
#define LVTS_EDATA03(__base) (__base + 0x0060)
+#define LVTS_MSROFT(__base) (__base + 0x006C)
+#define LVTS_ATP0(__base) (__base + 0x0070)
+#define LVTS_ATP1(__base) (__base + 0x0074)
+#define LVTS_ATP2(__base) (__base + 0x0078)
+#define LVTS_ATP3(__base) (__base + 0x007C)
#define LVTS_MSR0(__base) (__base + 0x0090)
#define LVTS_MSR1(__base) (__base + 0x0094)
#define LVTS_MSR2(__base) (__base + 0x0098)
@@ -65,12 +70,15 @@
#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_MONINT_OFFSET_HIGH_INTEN_SENSOR0 BIT(3)
+#define LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR1 BIT(8)
+#define LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR2 BIT(13)
+#define LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR3 BIT(25)
+#define LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR0 BIT(2)
+#define LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR1 BIT(7)
+#define LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR2 BIT(12)
+#define LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR3 BIT(24)
#define LVTS_INT_SENSOR0 0x0009001F
#define LVTS_INT_SENSOR1 0x001203E0
@@ -82,32 +90,43 @@
#define LVTS_GOLDEN_TEMP_DEFAULT 50
#define LVTS_COEFF_A_MT8195 -250460
#define LVTS_COEFF_B_MT8195 250460
+#define LVTS_COEFF_A_MT7987 -204650
+#define LVTS_COEFF_B_MT7987 204650
#define LVTS_COEFF_A_MT7988 -204650
#define LVTS_COEFF_B_MT7988 204650
+#define LVTS_COEFF_A_MT8196 391460
+#define LVTS_COEFF_B_MT8196 -391460
-#define LVTS_MSR_IMMEDIATE_MODE 0
-#define LVTS_MSR_FILTERED_MODE 1
+#define LVTS_MSR_OFFSET_MT8196 -984
#define LVTS_MSR_READ_TIMEOUT_US 400
#define LVTS_MSR_READ_WAIT_US (LVTS_MSR_READ_TIMEOUT_US / 2)
-#define LVTS_HW_TSHUT_TEMP 105000
-
#define LVTS_MINIMUM_THRESHOLD 20000
+#define LVTS_MAX_CAL_OFFSETS 3
+#define LVTS_NUM_CAL_OFFSETS_MT7988 3
+#define LVTS_NUM_CAL_OFFSETS_MT8196 2
+
static int golden_temp = LVTS_GOLDEN_TEMP_DEFAULT;
static int golden_temp_offset;
+enum lvts_msr_mode {
+ LVTS_MSR_IMMEDIATE_MODE,
+ LVTS_MSR_FILTERED_MODE,
+ LVTS_MSR_ATP_MODE,
+};
+
struct lvts_sensor_data {
int dt_id;
- u8 cal_offsets[3];
+ u8 cal_offsets[LVTS_MAX_CAL_OFFSETS];
};
struct lvts_ctrl_data {
struct lvts_sensor_data lvts_sensor[LVTS_SENSOR_MAX];
u8 valid_sensor_mask;
int offset;
- int mode;
+ enum lvts_msr_mode mode;
};
#define VALID_SENSOR_MAP(s0, s1, s2, s3) \
@@ -122,13 +141,25 @@ struct lvts_ctrl_data {
continue; \
else
+struct lvts_platform_ops {
+ int (*lvts_raw_to_temp)(u32 raw_temp, int temp_factor);
+ u32 (*lvts_temp_to_raw)(int temperature, int temp_factor);
+};
+
struct lvts_data {
const struct lvts_ctrl_data *lvts_ctrl;
+ const struct lvts_platform_ops *ops;
+ const u32 *conn_cmd;
+ const u32 *init_cmd;
+ int num_cal_offsets;
int num_lvts_ctrl;
+ int num_conn_cmd;
+ int num_init_cmd;
int temp_factor;
int temp_offset;
int gt_calib_bit_offset;
unsigned int def_calibration;
+ u16 msr_offset;
};
struct lvts_sensor {
@@ -145,7 +176,6 @@ struct lvts_ctrl {
struct lvts_sensor sensors[LVTS_SENSOR_MAX];
const struct lvts_data *lvts_data;
u32 calibration[LVTS_SENSOR_MAX];
- u32 hw_tshut_raw_temp;
u8 valid_sensor_mask;
int mode;
void __iomem *base;
@@ -198,6 +228,11 @@ static const struct debugfs_reg32 lvts_regs[] = {
LVTS_DEBUG_FS_REGS(LVTS_EDATA01),
LVTS_DEBUG_FS_REGS(LVTS_EDATA02),
LVTS_DEBUG_FS_REGS(LVTS_EDATA03),
+ LVTS_DEBUG_FS_REGS(LVTS_MSROFT),
+ LVTS_DEBUG_FS_REGS(LVTS_ATP0),
+ LVTS_DEBUG_FS_REGS(LVTS_ATP1),
+ LVTS_DEBUG_FS_REGS(LVTS_ATP2),
+ LVTS_DEBUG_FS_REGS(LVTS_ATP3),
LVTS_DEBUG_FS_REGS(LVTS_MSR0),
LVTS_DEBUG_FS_REGS(LVTS_MSR1),
LVTS_DEBUG_FS_REGS(LVTS_MSR2),
@@ -213,6 +248,13 @@ static const struct debugfs_reg32 lvts_regs[] = {
LVTS_DEBUG_FS_REGS(LVTS_CLKEN),
};
+static void lvts_debugfs_exit(void *data)
+{
+ struct lvts_domain *lvts_td = data;
+
+ debugfs_remove_recursive(lvts_td->dom_dentry);
+}
+
static int lvts_debugfs_init(struct device *dev, struct lvts_domain *lvts_td)
{
struct debugfs_regset32 *regset;
@@ -245,12 +287,7 @@ static int lvts_debugfs_init(struct device *dev, struct lvts_domain *lvts_td)
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);
+ return devm_add_action_or_reset(dev, lvts_debugfs_exit, lvts_td);
}
#else
@@ -261,11 +298,19 @@ static inline int lvts_debugfs_init(struct device *dev,
return 0;
}
-static void lvts_debugfs_exit(struct lvts_domain *lvts_td) { }
-
#endif
-static int lvts_raw_to_temp(u32 raw_temp, int temp_factor)
+static int lvts_raw_to_temp(u32 raw_temp, const struct lvts_data *lvts_data)
+{
+ return lvts_data->ops->lvts_raw_to_temp(raw_temp & 0xFFFF, lvts_data->temp_factor);
+}
+
+static u32 lvts_temp_to_raw(int temperature, const struct lvts_data *lvts_data)
+{
+ return lvts_data->ops->lvts_temp_to_raw(temperature, lvts_data->temp_factor);
+}
+
+static int lvts_raw_to_temp_mt7988(u32 raw_temp, int temp_factor)
{
int temperature;
@@ -275,7 +320,7 @@ static int lvts_raw_to_temp(u32 raw_temp, int temp_factor)
return temperature;
}
-static u32 lvts_temp_to_raw(int temperature, int temp_factor)
+static u32 lvts_temp_to_raw_mt7988(int temperature, int temp_factor)
{
u32 raw_temp = ((s64)(golden_temp_offset - temperature)) << 14;
@@ -284,6 +329,15 @@ static u32 lvts_temp_to_raw(int temperature, int temp_factor)
return raw_temp;
}
+static u32 lvts_temp_to_raw_mt8196(int temperature, int temp_factor)
+{
+ u32 raw_temp;
+
+ raw_temp = temperature - golden_temp_offset;
+
+ return div_s64((s64)temp_factor << 14, raw_temp);
+}
+
static int lvts_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct lvts_sensor *lvts_sensor = thermal_zone_device_priv(tz);
@@ -322,30 +376,48 @@ static int lvts_get_temp(struct thermal_zone_device *tz, int *temp)
if (rc)
return -EAGAIN;
- *temp = lvts_raw_to_temp(value & 0xFFFF, lvts_data->temp_factor);
+ *temp = lvts_raw_to_temp(value, lvts_data);
return 0;
}
static void lvts_update_irq_mask(struct lvts_ctrl *lvts_ctrl)
{
- static const u32 masks[] = {
- LVTS_MONINT_OFFSET_SENSOR0,
- LVTS_MONINT_OFFSET_SENSOR1,
- LVTS_MONINT_OFFSET_SENSOR2,
- LVTS_MONINT_OFFSET_SENSOR3,
+ static const u32 high_offset_inten_masks[] = {
+ LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR0,
+ LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR1,
+ LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR2,
+ LVTS_MONINT_OFFSET_HIGH_INTEN_SENSOR3,
+ };
+ static const u32 low_offset_inten_masks[] = {
+ LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR0,
+ LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR1,
+ LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR2,
+ LVTS_MONINT_OFFSET_LOW_INTEN_SENSOR3,
};
u32 value = 0;
int i;
value = readl(LVTS_MONINT(lvts_ctrl->base));
- for (i = 0; i < ARRAY_SIZE(masks); i++) {
+ lvts_for_each_valid_sensor(i, lvts_ctrl) {
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];
+ && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) {
+ /*
+ * The minimum threshold needs to be configured in the
+ * OFFSETL register to get working interrupts, but we
+ * don't actually want to generate interrupts when
+ * crossing it.
+ */
+ if (lvts_ctrl->low_thresh == -INT_MAX) {
+ value &= ~low_offset_inten_masks[i];
+ value |= high_offset_inten_masks[i];
+ } else {
+ value |= low_offset_inten_masks[i] | high_offset_inten_masks[i];
+ }
+ } else {
+ value &= ~(low_offset_inten_masks[i] | high_offset_inten_masks[i]);
+ }
}
writel(value, LVTS_MONINT(lvts_ctrl->base));
@@ -374,8 +446,8 @@ static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high)
const struct lvts_data *lvts_data = lvts_ctrl->lvts_data;
void __iomem *base = lvts_sensor->base;
u32 raw_low = lvts_temp_to_raw(low != -INT_MAX ? low : LVTS_MINIMUM_THRESHOLD,
- lvts_data->temp_factor);
- u32 raw_high = lvts_temp_to_raw(high, lvts_data->temp_factor);
+ lvts_data);
+ u32 raw_high = lvts_temp_to_raw(high, lvts_data);
bool should_update_thresh;
lvts_sensor->low_thresh = low;
@@ -553,7 +625,7 @@ static irqreturn_t lvts_irq_handler(int irq, void *data)
return iret;
}
-static struct thermal_zone_device_ops lvts_ops = {
+static const struct thermal_zone_device_ops lvts_ops = {
.get_temp = lvts_get_temp,
.set_trips = lvts_set_trips,
};
@@ -577,6 +649,13 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl,
LVTS_IMMD3(lvts_ctrl->base)
};
+ void __iomem *atp_regs[] = {
+ LVTS_ATP0(lvts_ctrl->base),
+ LVTS_ATP1(lvts_ctrl->base),
+ LVTS_ATP2(lvts_ctrl->base),
+ LVTS_ATP3(lvts_ctrl->base)
+ };
+
int i;
lvts_for_each_valid_sensor(i, lvts_ctrl_data) {
@@ -612,18 +691,50 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl,
/*
* 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];
+ switch (lvts_ctrl_data->mode) {
+ case LVTS_MSR_IMMEDIATE_MODE:
+ lvts_sensor[i].msr = imm_regs[i];
+ break;
+ case LVTS_MSR_FILTERED_MODE:
+ lvts_sensor[i].msr = msr_regs[i];
+ break;
+ case LVTS_MSR_ATP_MODE:
+ lvts_sensor[i].msr = atp_regs[i];
+ break;
+ default:
+ lvts_sensor[i].msr = imm_regs[i];
+ break;
+ }
lvts_sensor[i].low_thresh = INT_MIN;
lvts_sensor[i].high_thresh = INT_MIN;
- };
+ }
lvts_ctrl->valid_sensor_mask = lvts_ctrl_data->valid_sensor_mask;
return 0;
}
+static int lvts_decode_sensor_calibration(const struct lvts_sensor_data *sensor,
+ const u8 *efuse_calibration, u32 calib_len,
+ u8 num_offsets, u32 *calib)
+{
+ int i;
+ u32 calib_val = 0;
+
+ for (i = 0; i < num_offsets; i++) {
+ u8 offset = sensor->cal_offsets[i];
+
+ if (offset >= calib_len)
+ return -EINVAL;
+ // Pack each calibration byte into the correct position
+ calib_val |= efuse_calibration[offset] << (8 * i);
+ }
+
+ *calib = calib_val;
+ return 0;
+}
+
/*
* The efuse blob values follows the sensor enumeration per thermal
* controller. The decoding of the stream is as follow:
@@ -680,6 +791,39 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl,
* <-----ap--tc#3-----> <-----sensor#7-----> <-----sensor#8----->
* 0x40 | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48
*
+ * MT8196 :
+ * Stream index map for MCU Domain mt8196 :
+ *
+ * <-sensor#1--> <-sensor#0--> <-sensor#3--> <-sensor#2-->
+ * 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x09 | 0x0A | 0x0B
+ *
+ * <-sensor#5--> <-sensor#4--> <-sensor#7--> <-sensor#6-->
+ * 0x0C | 0x0D | 0x0E | 0x0F | 0x10 | 0x11 | 0x12 | 0x13
+ *
+ * <-sensor#9--> <-sensor#8--> <-sensor#11-> <-sensor#10->
+ * 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 | 0x1A | 0X1B
+ *
+ * <-sensor#13-> <-sensor#12-> <-sensor#15-> <-sensor#14->
+ * 0x1C | 0x1D | 0x1E | 0x1F | 0x20 | 0x21 | 0x22 | 0x23
+ *
+ * Stream index map for APU Domain mt8196 :
+ *
+ * <-sensor#1--> <-sensor#0--> <-sensor#3--> <-sensor#2-->
+ * 0x24 | 0x25 | 0x26 | 0x27 | 0x28 | 0x29 | 0x2A | 0x2B
+ *
+ * Stream index map for GPU Domain mt8196 :
+ *
+ * <-sensor#1--> <-sensor#0-->
+ * 0x2C | 0x2D | 0x2E | 0x2F
+ *
+ * Stream index map for AP Domain mt8196 :
+ *
+ * <-sensor#1--> <-sensor#0--> <-sensor#3--> <-sensor#2-->
+ * 0x30 | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37
+ *
+ * <-sensor#5--> <-sensor#4--> <-sensor#6--> <-sensor#7-->
+ * 0x38 | 0x39 | 0x3A | 0x3B | 0x3C | 0x3D | 0x3E | 0x3F
+ *
* Note: In some cases, values don't strictly follow a little endian ordering.
* The data description gives byte offsets constituting each calibration value
* for each sensor.
@@ -689,26 +833,29 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl
u8 *efuse_calibration,
size_t calib_len)
{
- int i;
+ const struct lvts_data *lvts_data = lvts_ctrl->lvts_data;
+ int i, ret;
u32 gt;
/* A zero value for gt means that device has invalid efuse data */
- gt = (((u32 *)efuse_calibration)[0] >> lvts_ctrl->lvts_data->gt_calib_bit_offset) & 0xff;
+ gt = (((u32 *)efuse_calibration)[0] >> lvts_data->gt_calib_bit_offset) & 0xff;
lvts_for_each_valid_sensor(i, lvts_ctrl_data) {
const struct lvts_sensor_data *sensor =
&lvts_ctrl_data->lvts_sensor[i];
+ u32 calib = 0;
- if (sensor->cal_offsets[0] >= calib_len ||
- sensor->cal_offsets[1] >= calib_len ||
- sensor->cal_offsets[2] >= calib_len)
- return -EINVAL;
+ ret = lvts_decode_sensor_calibration(sensor, efuse_calibration,
+ calib_len,
+ lvts_data->num_cal_offsets,
+ &calib);
+ if (ret)
+ return ret;
if (gt) {
- lvts_ctrl->calibration[i] =
- (efuse_calibration[sensor->cal_offsets[0]] << 0) +
- (efuse_calibration[sensor->cal_offsets[1]] << 8) +
- (efuse_calibration[sensor->cal_offsets[2]] << 16);
+ lvts_ctrl->calibration[i] = calib;
+ if (lvts_ctrl->lvts_data->msr_offset)
+ lvts_ctrl->calibration[i] += lvts_ctrl->lvts_data->msr_offset;
} else if (lvts_ctrl->lvts_data->def_calibration) {
lvts_ctrl->calibration[i] = lvts_ctrl->lvts_data->def_calibration;
} else {
@@ -837,14 +984,6 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td,
*/
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_HW_TSHUT_TEMP,
- lvts_data->temp_factor);
-
lvts_ctrl[i].low_thresh = INT_MIN;
lvts_ctrl[i].high_thresh = INT_MIN;
}
@@ -860,13 +999,39 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td,
return 0;
}
+static void lvts_ctrl_monitor_enable(struct device *dev, struct lvts_ctrl *lvts_ctrl, bool enable)
+{
+ /*
+ * Bitmaps to enable each sensor on filtered mode in the MONCTL0
+ * register.
+ */
+ static const u8 sensor_filt_bitmap[] = { BIT(0), BIT(1), BIT(2), BIT(3) };
+ u32 sensor_map = 0;
+ int i;
+
+ if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE)
+ return;
+
+ if (enable) {
+ lvts_for_each_valid_sensor(i, lvts_ctrl)
+ sensor_map |= sensor_filt_bitmap[i];
+ }
+
+ /*
+ * Bits:
+ * 9: Single point access flow
+ * 0-3: Enable sensing point 0-3
+ */
+ writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
+}
+
/*
* 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)
+static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, const u32 *cmds, int nr_cmds)
{
int i;
@@ -893,7 +1058,6 @@ static int lvts_irq_init(struct lvts_ctrl *lvts_ctrl)
* 10 : Selected sensor with bits 19-18
* 11 : Reserved
*/
- writel(BIT(16), LVTS_PROTCTL(lvts_ctrl->base));
/*
* LVTS_PROTTA : Stage 1 temperature threshold
@@ -906,8 +1070,8 @@ static int lvts_irq_init(struct lvts_ctrl *lvts_ctrl)
*
* writel(0x0, LVTS_PROTTA(lvts_ctrl->base));
* writel(0x0, LVTS_PROTTB(lvts_ctrl->base));
+ * writel(0x0, LVTS_PROTTC(lvts_ctrl->base));
*/
- writel(lvts_ctrl->hw_tshut_raw_temp, LVTS_PROTTC(lvts_ctrl->base));
/*
* LVTS_MONINT : Interrupt configuration register
@@ -915,7 +1079,7 @@ static int lvts_irq_init(struct lvts_ctrl *lvts_ctrl)
* 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));
+ writel(0, LVTS_MONINT(lvts_ctrl->base));
return 0;
}
@@ -950,9 +1114,10 @@ static int lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable)
static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
{
- u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 };
+ const struct lvts_data *lvts_data = lvts_ctrl->lvts_data;
+ u32 id;
- lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
+ lvts_write_config(lvts_ctrl, lvts_data->conn_cmd, lvts_data->num_conn_cmd);
/*
* LVTS_ID : Get ID and status of the thermal controller
@@ -971,17 +1136,9 @@ static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl)
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
- };
+ const struct lvts_data *lvts_data = lvts_ctrl->lvts_data;
- lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds));
+ lvts_write_config(lvts_ctrl, lvts_data->init_cmd, lvts_data->num_init_cmd);
return 0;
}
@@ -1006,6 +1163,17 @@ static int lvts_ctrl_calibrate(struct device *dev, struct lvts_ctrl *lvts_ctrl)
for (i = 0; i < LVTS_SENSOR_MAX; i++)
writel(lvts_ctrl->calibration[i], lvts_edata[i]);
+ /* LVTS_MSROFT : Constant offset applied to MSR values
+ * for post-processing
+ *
+ * Bits:
+ *
+ * 20-0 : Constant data added to MSR values
+ */
+ if (lvts_ctrl->lvts_data->msr_offset)
+ writel(lvts_ctrl->lvts_data->msr_offset,
+ LVTS_MSROFT(lvts_ctrl->base));
+
return 0;
}
@@ -1339,10 +1507,22 @@ static void lvts_remove(struct platform_device *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);
}
+static const struct lvts_ctrl_data mt7987_lvts_ap_data_ctrl[] = {
+ {
+ .lvts_sensor = {
+ { .dt_id = MT7987_CPU,
+ .cal_offsets = { 0x04, 0x05, 0x06 } },
+ { .dt_id = MT7987_ETH2P5G,
+ .cal_offsets = { 0x08, 0x09, 0x0a } },
+ },
+ VALID_SENSOR_MAP(1, 1, 0, 0),
+ .offset = 0x0,
+ .mode = LVTS_MSR_FILTERED_MODE,
+ },
+};
+
static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = {
{
.lvts_sensor = {
@@ -1381,8 +1561,11 @@ static int lvts_suspend(struct device *dev)
lvts_td = dev_get_drvdata(dev);
- for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
+ lvts_ctrl_monitor_enable(dev, &lvts_td->lvts_ctrl[i], false);
+ usleep_range(100, 200);
lvts_ctrl_set_enable(&lvts_td->lvts_ctrl[i], false);
+ }
clk_disable_unprepare(lvts_td->clk);
@@ -1400,12 +1583,40 @@ static int lvts_resume(struct device *dev)
if (ret)
return ret;
- for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
+ for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
lvts_ctrl_set_enable(&lvts_td->lvts_ctrl[i], true);
+ usleep_range(100, 200);
+ lvts_ctrl_monitor_enable(dev, &lvts_td->lvts_ctrl[i], true);
+ }
return 0;
}
+static const u32 default_conn_cmds[] = { 0xC103FFFF, 0xC502FF55 };
+static const u32 mt7988_conn_cmds[] = { 0xC103FFFF, 0xC502FC55 };
+
+/*
+ * Write device mask: 0xC1030000
+ */
+static const u32 default_init_cmds[] = {
+ 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1,
+ 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300,
+ 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC,
+ 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1
+};
+
+static const u32 mt7987_init_cmds[] = {
+ 0xC1030300, 0xC1030420, 0xC1030500, 0xC10307A6, 0xC10308C7,
+ 0xC103098D, 0xC1030C7C, 0xC1030AA8, 0xC10308CE, 0xC10308C7,
+ 0xC1030B04, 0xC1030E01, 0xC10306B8
+};
+
+static const u32 mt7988_init_cmds[] = {
+ 0xC1030300, 0xC1030420, 0xC1030500, 0xC10307A6, 0xC1030CFC,
+ 0xC1030A8C, 0xC103098D, 0xC10308F1, 0xC1030B04, 0xC1030E01,
+ 0xC10306B8
+};
+
/*
* The MT8186 calibration data is stored as packed 3-byte little-endian
* values using a weird layout that makes sense only when viewed as a 32-bit
@@ -1698,78 +1909,270 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = {
}
};
+static const struct lvts_ctrl_data mt8196_lvts_mcu_data_ctrl[] = {
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8196_MCU_MEDIUM_CPU6_0,
+ .cal_offsets = { 0x06, 0x07 } },
+ { .dt_id = MT8196_MCU_MEDIUM_CPU6_1,
+ .cal_offsets = { 0x04, 0x05 } },
+ { .dt_id = MT8196_MCU_DSU2,
+ .cal_offsets = { 0x0A, 0x0B } },
+ { .dt_id = MT8196_MCU_DSU3,
+ .cal_offsets = { 0x08, 0x09 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x0,
+ .mode = LVTS_MSR_ATP_MODE,
+ },
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8196_MCU_LITTLE_CPU3,
+ .cal_offsets = { 0x0E, 0x0F } },
+ { .dt_id = MT8196_MCU_LITTLE_CPU0,
+ .cal_offsets = { 0x0C, 0x0D } },
+ { .dt_id = MT8196_MCU_LITTLE_CPU1,
+ .cal_offsets = { 0x12, 0x13 } },
+ { .dt_id = MT8196_MCU_LITTLE_CPU2,
+ .cal_offsets = { 0x10, 0x11 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x100,
+ .mode = LVTS_MSR_ATP_MODE,
+ },
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8196_MCU_MEDIUM_CPU4_0,
+ .cal_offsets = { 0x16, 0x17 } },
+ { .dt_id = MT8196_MCU_MEDIUM_CPU4_1,
+ .cal_offsets = { 0x14, 0x15 } },
+ { .dt_id = MT8196_MCU_MEDIUM_CPU5_0,
+ .cal_offsets = { 0x1A, 0x1B } },
+ { .dt_id = MT8196_MCU_MEDIUM_CPU5_1,
+ .cal_offsets = { 0x18, 0x19 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x200,
+ .mode = LVTS_MSR_ATP_MODE,
+ },
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8196_MCU_DSU0,
+ .cal_offsets = { 0x1E, 0x1F } },
+ { .dt_id = MT8196_MCU_DSU1,
+ .cal_offsets = { 0x1C, 0x1D } },
+ { .dt_id = MT8196_MCU_BIG_CPU7_0,
+ .cal_offsets = { 0x22, 0x23 } },
+ { .dt_id = MT8196_MCU_BIG_CPU7_1,
+ .cal_offsets = { 0x20, 0x21 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x300,
+ .mode = LVTS_MSR_ATP_MODE,
+ }
+};
+
+static const struct lvts_ctrl_data mt8196_lvts_ap_data_ctrl[] = {
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8196_AP_TOP0,
+ .cal_offsets = { 0x32, 0x33 } },
+ { .dt_id = MT8196_AP_TOP1,
+ .cal_offsets = { 0x30, 0x31 } },
+ { .dt_id = MT8196_AP_TOP2,
+ .cal_offsets = { 0x36, 0x37 } },
+ { .dt_id = MT8196_AP_TOP3,
+ .cal_offsets = { 0x34, 0x35 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x0,
+ .mode = LVTS_MSR_ATP_MODE,
+ },
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8196_AP_BOT0,
+ .cal_offsets = { 0x3A, 0x3B } },
+ { .dt_id = MT8196_AP_BOT1,
+ .cal_offsets = { 0x38, 0x39 } },
+ { .dt_id = MT8196_AP_BOT2,
+ .cal_offsets = { 0x3E, 0x3F } },
+ { .dt_id = MT8196_AP_BOT3,
+ .cal_offsets = { 0x3C, 0x3D } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x100,
+ .mode = LVTS_MSR_ATP_MODE,
+ }
+};
+
+static const struct lvts_platform_ops lvts_platform_ops_mt7988 = {
+ .lvts_raw_to_temp = lvts_raw_to_temp_mt7988,
+ .lvts_temp_to_raw = lvts_temp_to_raw_mt7988,
+};
+
+static const struct lvts_platform_ops lvts_platform_ops_mt8196 = {
+ .lvts_raw_to_temp = lvts_raw_to_temp_mt7988,
+ .lvts_temp_to_raw = lvts_temp_to_raw_mt8196,
+};
+
+static const struct lvts_data mt7987_lvts_ap_data = {
+ .lvts_ctrl = mt7987_lvts_ap_data_ctrl,
+ .num_lvts_ctrl = ARRAY_SIZE(mt7987_lvts_ap_data_ctrl),
+ .conn_cmd = mt7988_conn_cmds,
+ .init_cmd = mt7987_init_cmds,
+ .num_conn_cmd = ARRAY_SIZE(mt7988_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(mt7987_init_cmds),
+ .temp_factor = LVTS_COEFF_A_MT7987,
+ .temp_offset = LVTS_COEFF_B_MT7987,
+ .gt_calib_bit_offset = 32,
+ .def_calibration = 19380,
+};
+
static const struct lvts_data mt7988_lvts_ap_data = {
.lvts_ctrl = mt7988_lvts_ap_data_ctrl,
+ .conn_cmd = mt7988_conn_cmds,
+ .init_cmd = mt7988_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt7988_lvts_ap_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(mt7988_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(mt7988_init_cmds),
.temp_factor = LVTS_COEFF_A_MT7988,
.temp_offset = LVTS_COEFF_B_MT7988,
.gt_calib_bit_offset = 24,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8186_lvts_data = {
.lvts_ctrl = mt8186_lvts_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8186_lvts_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT7988,
.temp_offset = LVTS_COEFF_B_MT7988,
.gt_calib_bit_offset = 24,
.def_calibration = 19000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8188_lvts_mcu_data = {
.lvts_ctrl = mt8188_lvts_mcu_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_mcu_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 20,
.def_calibration = 35000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8188_lvts_ap_data = {
.lvts_ctrl = mt8188_lvts_ap_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_ap_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 20,
.def_calibration = 35000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8192_lvts_mcu_data = {
.lvts_ctrl = mt8192_lvts_mcu_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
.def_calibration = 35000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8192_lvts_ap_data = {
.lvts_ctrl = mt8192_lvts_ap_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_ap_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
.def_calibration = 35000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8195_lvts_mcu_data = {
.lvts_ctrl = mt8195_lvts_mcu_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_mcu_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
.def_calibration = 35000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
};
static const struct lvts_data mt8195_lvts_ap_data = {
.lvts_ctrl = mt8195_lvts_ap_data_ctrl,
+ .conn_cmd = default_conn_cmds,
+ .init_cmd = default_init_cmds,
.num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_ap_data_ctrl),
+ .num_conn_cmd = ARRAY_SIZE(default_conn_cmds),
+ .num_init_cmd = ARRAY_SIZE(default_init_cmds),
.temp_factor = LVTS_COEFF_A_MT8195,
.temp_offset = LVTS_COEFF_B_MT8195,
.gt_calib_bit_offset = 24,
.def_calibration = 35000,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT7988,
+ .ops = &lvts_platform_ops_mt7988,
+};
+
+static const struct lvts_data mt8196_lvts_mcu_data = {
+ .lvts_ctrl = mt8196_lvts_mcu_data_ctrl,
+ .num_lvts_ctrl = ARRAY_SIZE(mt8196_lvts_mcu_data_ctrl),
+ .temp_factor = LVTS_COEFF_A_MT8196,
+ .temp_offset = LVTS_COEFF_B_MT8196,
+ .gt_calib_bit_offset = 0,
+ .def_calibration = 14437,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT8196,
+ .msr_offset = LVTS_MSR_OFFSET_MT8196,
+ .ops = &lvts_platform_ops_mt8196,
+};
+
+static const struct lvts_data mt8196_lvts_ap_data = {
+ .lvts_ctrl = mt8196_lvts_ap_data_ctrl,
+ .num_lvts_ctrl = ARRAY_SIZE(mt8196_lvts_ap_data_ctrl),
+ .temp_factor = LVTS_COEFF_A_MT8196,
+ .temp_offset = LVTS_COEFF_B_MT8196,
+ .gt_calib_bit_offset = 0,
+ .def_calibration = 14437,
+ .num_cal_offsets = LVTS_NUM_CAL_OFFSETS_MT8196,
+ .msr_offset = LVTS_MSR_OFFSET_MT8196,
+ .ops = &lvts_platform_ops_mt8196,
};
static const struct of_device_id lvts_of_match[] = {
+ { .compatible = "mediatek,mt7987-lvts-ap", .data = &mt7987_lvts_ap_data },
{ .compatible = "mediatek,mt7988-lvts-ap", .data = &mt7988_lvts_ap_data },
{ .compatible = "mediatek,mt8186-lvts", .data = &mt8186_lvts_data },
{ .compatible = "mediatek,mt8188-lvts-mcu", .data = &mt8188_lvts_mcu_data },
@@ -1778,6 +2181,8 @@ static const struct of_device_id lvts_of_match[] = {
{ .compatible = "mediatek,mt8192-lvts-ap", .data = &mt8192_lvts_ap_data },
{ .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data },
{ .compatible = "mediatek,mt8195-lvts-ap", .data = &mt8195_lvts_ap_data },
+ { .compatible = "mediatek,mt8196-lvts-mcu", .data = &mt8196_lvts_mcu_data },
+ { .compatible = "mediatek,mt8196-lvts-ap", .data = &mt8196_lvts_ap_data },
{},
};
MODULE_DEVICE_TABLE(of, lvts_of_match);
diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig
index 2c7f3f9a26eb..a6bb01082ec6 100644
--- a/drivers/thermal/qcom/Kconfig
+++ b/drivers/thermal/qcom/Kconfig
@@ -34,7 +34,8 @@ config QCOM_SPMI_TEMP_ALARM
config QCOM_LMH
tristate "Qualcomm Limits Management Hardware"
- depends on ARCH_QCOM && QCOM_SCM
+ depends on ARCH_QCOM || COMPILE_TEST
+ select QCOM_SCM
help
This enables initialization of Qualcomm limits management
hardware(LMh). LMh allows for hardware-enforced mitigation for cpus based on
diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c
index d2d49264cf83..3d072b7a4a6d 100644
--- a/drivers/thermal/qcom/lmh.c
+++ b/drivers/thermal/qcom/lmh.c
@@ -5,6 +5,8 @@
*/
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -204,12 +206,12 @@ static int lmh_probe(struct platform_device *pdev)
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_LOW_THRESHOLD, temp_low,
LMH_NODE_DCVS, node_id, 0);
if (ret) {
- dev_err(dev, "Error setting thermal ARM threshold%d\n", ret);
+ dev_err(dev, "Error setting thermal LOW threshold%d\n", ret);
return ret;
}
lmh_data->irq = platform_get_irq(pdev, 0);
- lmh_data->domain = irq_domain_add_linear(np, 1, &lmh_irq_ops, lmh_data);
+ lmh_data->domain = irq_domain_create_linear(dev_fwnode(dev), 1, &lmh_irq_ops, lmh_data);
if (!lmh_data->domain) {
dev_err(dev, "Error adding irq_domain\n");
return -EINVAL;
@@ -218,7 +220,7 @@ static int lmh_probe(struct platform_device *pdev)
/* Disable the irq and let cpufreq enable it when ready to handle the interrupt */
irq_set_status_flags(lmh_data->irq, IRQ_NOAUTOEN);
ret = devm_request_irq(dev, lmh_data->irq, lmh_handle_irq,
- IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ IRQF_NO_THREAD | IRQF_NO_SUSPEND,
"lmh-irq", lmh_data);
if (ret) {
dev_err(dev, "Error %d registering irq %x\n", ret, lmh_data->irq);
diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
index c2d59cbfaea9..f39ca0ddd17b 100644
--- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2011-2015, 2017, 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -16,31 +18,51 @@
#include "../thermal_hwmon.h"
+#define QPNP_TM_REG_DIG_MINOR 0x00
#define QPNP_TM_REG_DIG_MAJOR 0x01
#define QPNP_TM_REG_TYPE 0x04
#define QPNP_TM_REG_SUBTYPE 0x05
#define QPNP_TM_REG_STATUS 0x08
+#define QPNP_TM_REG_IRQ_STATUS 0x10
#define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
#define QPNP_TM_REG_ALARM_CTRL 0x46
+/* TEMP_DAC_STGx registers are only present for TEMP_GEN2 v2.0 */
+#define QPNP_TM_REG_TEMP_DAC_STG1 0x47
+#define QPNP_TM_REG_TEMP_DAC_STG2 0x48
+#define QPNP_TM_REG_TEMP_DAC_STG3 0x49
+#define QPNP_TM_REG_LITE_TEMP_CFG1 0x50
+#define QPNP_TM_REG_LITE_TEMP_CFG2 0x51
+
#define QPNP_TM_TYPE 0x09
#define QPNP_TM_SUBTYPE_GEN1 0x08
#define QPNP_TM_SUBTYPE_GEN2 0x09
+#define QPNP_TM_SUBTYPE_LITE 0xC0
#define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
-#define STATUS_GEN2_STATE_SHIFT 4
-#define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6)
+/* IRQ status only needed for TEMP_ALARM_LITE */
+#define IRQ_STATUS_MASK BIT(0)
+
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6)
#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
#define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
#define ALARM_CTRL_FORCE_ENABLE BIT(7)
+#define LITE_TEMP_CFG_THRESHOLD_MASK GENMASK(3, 2)
+
#define THRESH_COUNT 4
#define STAGE_COUNT 3
+enum overtemp_stage {
+ STAGE1 = 0,
+ STAGE2,
+ STAGE3,
+};
+
/* Over-temperature trip point values in mC */
static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = {
{ 105000, 125000, 145000 },
@@ -63,24 +85,68 @@ static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
#define TEMP_STAGE_HYSTERESIS 2000
+/*
+ * For TEMP_GEN2 v2.0, TEMP_DAC_STG1/2/3 registers are used to set the threshold
+ * for each stage independently.
+ * TEMP_DAC_STG* = 0 --> 80 C
+ * Each 8 step increase in TEMP_DAC_STG* value corresponds to 5 C (5000 mC).
+ */
+#define TEMP_DAC_MIN 80000
+#define TEMP_DAC_SCALE_NUM 8
+#define TEMP_DAC_SCALE_DEN 5000
+
+#define TEMP_DAC_TEMP_TO_REG(temp) \
+ (((temp) - TEMP_DAC_MIN) * TEMP_DAC_SCALE_NUM / TEMP_DAC_SCALE_DEN)
+#define TEMP_DAC_REG_TO_TEMP(reg) \
+ (TEMP_DAC_MIN + (reg) * TEMP_DAC_SCALE_DEN / TEMP_DAC_SCALE_NUM)
+
+static const long temp_dac_max[STAGE_COUNT] = {
+ 119375, 159375, 159375
+};
+
+/*
+ * TEMP_ALARM_LITE has two stages: warning and shutdown with independently
+ * configured threshold temperatures.
+ */
+
+static const long temp_lite_warning_map[THRESH_COUNT] = {
+ 115000, 125000, 135000, 145000
+};
+
+static const long temp_lite_shutdown_map[THRESH_COUNT] = {
+ 135000, 145000, 160000, 175000
+};
+
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
#define DEFAULT_TEMP 37000
+struct qpnp_tm_chip;
+
+struct spmi_temp_alarm_data {
+ const struct thermal_zone_device_ops *ops;
+ const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
+ int (*sync_thresholds)(struct qpnp_tm_chip *chip);
+ int (*get_temp_stage)(struct qpnp_tm_chip *chip);
+ int (*configure_trip_temps)(struct qpnp_tm_chip *chip);
+};
+
struct qpnp_tm_chip {
struct regmap *map;
struct device *dev;
struct thermal_zone_device *tz_dev;
+ const struct spmi_temp_alarm_data *data;
unsigned int subtype;
long temp;
- unsigned int thresh;
unsigned int stage;
unsigned int base;
+ unsigned int ntrips;
/* protects .thresh, .stage and chip registers */
struct mutex lock;
bool initialized;
+ bool require_stage2_shutdown;
+ long temp_thresh_map[STAGE_COUNT];
struct iio_channel *adc;
- const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
};
/* This array maps from GEN2 alarm state to GEN1 alarm stage */
@@ -114,34 +180,66 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
*/
static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
{
- if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0 ||
- stage > STAGE_COUNT)
+ if (stage == 0 || stage > STAGE_COUNT)
return 0;
- return (*chip->temp_map)[chip->thresh][stage - 1];
+ return chip->temp_thresh_map[stage - 1];
}
/**
- * qpnp_tm_get_temp_stage() - return over-temperature stage
+ * qpnp_tm_gen1_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
- * Return: stage (GEN1) or state (GEN2) on success, or errno on failure.
+ * Return: stage on success, or errno on failure.
*/
-static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
+static int qpnp_tm_gen1_get_temp_stage(struct qpnp_tm_chip *chip)
{
int ret;
- u8 reg = 0;
+ u8 reg;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
if (ret < 0)
return ret;
- if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
- ret = reg & STATUS_GEN1_STAGE_MASK;
- else
- ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT;
+ return FIELD_GET(STATUS_GEN1_STAGE_MASK, reg);
+}
- return ret;
+/**
+ * qpnp_tm_gen2_get_temp_stage() - return over-temperature stage
+ * @chip: Pointer to the qpnp_tm chip
+ *
+ * Return: stage on success, or errno on failure.
+ */
+static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip)
+{
+ int ret;
+ u8 reg;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+
+ ret = FIELD_GET(STATUS_GEN2_STATE_MASK, reg);
+
+ return alarm_state_map[ret];
+}
+
+/**
+ * qpnp_tm_lite_get_temp_stage() - return over-temperature stage
+ * @chip: Pointer to the qpnp_tm chip
+ *
+ * Return: alarm interrupt state on success, or errno on failure.
+ */
+static int qpnp_tm_lite_get_temp_stage(struct qpnp_tm_chip *chip)
+{
+ u8 reg = 0;
+ int ret;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_IRQ_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+
+ return FIELD_GET(IRQ_STATUS_MASK, reg);
}
/*
@@ -150,23 +248,16 @@ static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
*/
static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
{
- unsigned int stage, stage_new, stage_old;
+ unsigned int stage_new, stage_old;
int ret;
WARN_ON(!mutex_is_locked(&chip->lock));
- ret = qpnp_tm_get_temp_stage(chip);
+ ret = chip->data->get_temp_stage(chip);
if (ret < 0)
return ret;
- stage = ret;
-
- if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
- stage_new = stage;
- stage_old = chip->stage;
- } else {
- stage_new = alarm_state_map[stage];
- stage_old = alarm_state_map[chip->stage];
- }
+ stage_new = ret;
+ stage_old = chip->stage;
if (stage_new > stage_old) {
/* increasing stage, use lower bound */
@@ -178,7 +269,7 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
- TEMP_STAGE_HYSTERESIS;
}
- chip->stage = stage;
+ chip->stage = stage_new;
return 0;
}
@@ -218,35 +309,35 @@ static int qpnp_tm_get_temp(struct thermal_zone_device *tz, int *temp)
static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
int temp)
{
- long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1];
- long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1];
- bool disable_s2_shutdown = false;
- u8 reg;
+ long stage2_threshold_min = (*chip->data->temp_map)[THRESH_MIN][STAGE2];
+ long stage2_threshold_max = (*chip->data->temp_map)[THRESH_MAX][STAGE2];
+ bool disable_stage2_shutdown = false;
+ u8 reg, threshold;
WARN_ON(!mutex_is_locked(&chip->lock));
/*
- * Default: S2 and S3 shutdown enabled, thresholds at
+ * Default: Stage 2 and Stage 3 shutdown enabled, thresholds at
* lowest threshold set, monitoring at 25Hz
*/
reg = SHUTDOWN_CTRL1_RATE_25HZ;
if (temp == THERMAL_TEMP_INVALID ||
temp < stage2_threshold_min) {
- chip->thresh = THRESH_MIN;
+ threshold = THRESH_MIN;
goto skip;
}
if (temp <= stage2_threshold_max) {
- chip->thresh = THRESH_MAX -
+ threshold = THRESH_MAX -
((stage2_threshold_max - temp) /
TEMP_THRESH_STEP);
- disable_s2_shutdown = true;
+ disable_stage2_shutdown = true;
} else {
- chip->thresh = THRESH_MAX;
+ threshold = THRESH_MAX;
if (chip->adc)
- disable_s2_shutdown = true;
+ disable_stage2_shutdown = true;
else
dev_warn(chip->dev,
"No ADC is configured and critical temperature %d mC is above the maximum stage 2 threshold of %ld mC! Configuring stage 2 shutdown at %ld mC.\n",
@@ -254,9 +345,11 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
}
skip:
- reg |= chip->thresh;
- if (disable_s2_shutdown)
- reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
+ memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
+ sizeof(chip->temp_thresh_map));
+ reg |= threshold;
+ if (disable_stage2_shutdown && !chip->require_stage2_shutdown)
+ reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2;
return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
}
@@ -282,6 +375,146 @@ static const struct thermal_zone_device_ops qpnp_tm_sensor_ops = {
.set_trip_temp = qpnp_tm_set_trip_temp,
};
+static int qpnp_tm_gen2_rev2_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
+{
+ int ret, temp_cfg;
+ u8 reg;
+
+ WARN_ON(!mutex_is_locked(&chip->lock));
+
+ if (trip >= STAGE_COUNT) {
+ dev_err(chip->dev, "invalid TEMP_DAC trip = %d\n", trip);
+ return -EINVAL;
+ } else if (temp < TEMP_DAC_MIN || temp > temp_dac_max[trip]) {
+ dev_err(chip->dev, "invalid TEMP_DAC temp = %d\n", temp);
+ return -EINVAL;
+ }
+
+ reg = TEMP_DAC_TEMP_TO_REG(temp);
+ temp_cfg = TEMP_DAC_REG_TO_TEMP(reg);
+
+ ret = qpnp_tm_write(chip, QPNP_TM_REG_TEMP_DAC_STG1 + trip, reg);
+ if (ret < 0) {
+ dev_err(chip->dev, "TEMP_DAC_STG write failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ chip->temp_thresh_map[trip] = temp_cfg;
+
+ return 0;
+}
+
+static int qpnp_tm_gen2_rev2_set_trip_temp(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip, int temp)
+{
+ unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
+ struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, trip_index, temp);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static const struct thermal_zone_device_ops qpnp_tm_gen2_rev2_sensor_ops = {
+ .get_temp = qpnp_tm_get_temp,
+ .set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp,
+};
+
+static int qpnp_tm_lite_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
+{
+ int ret, temp_cfg, i;
+ const long *temp_map;
+ u8 reg, thresh;
+ u16 addr;
+
+ WARN_ON(!mutex_is_locked(&chip->lock));
+
+ if (trip >= STAGE_COUNT) {
+ dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
+ return -EINVAL;
+ }
+
+ switch (trip) {
+ case 0:
+ temp_map = temp_lite_warning_map;
+ addr = QPNP_TM_REG_LITE_TEMP_CFG1;
+ break;
+ case 1:
+ /*
+ * The second trip point is purely in software to facilitate
+ * a controlled shutdown after the warning threshold is crossed
+ * but before the automatic hardware shutdown threshold is
+ * crossed.
+ */
+ return 0;
+ case 2:
+ temp_map = temp_lite_shutdown_map;
+ addr = QPNP_TM_REG_LITE_TEMP_CFG2;
+ break;
+ default:
+ return 0;
+ }
+
+ if (temp < temp_map[THRESH_MIN] || temp > temp_map[THRESH_MAX]) {
+ dev_err(chip->dev, "invalid TEMP_LITE temp = %d\n", temp);
+ return -EINVAL;
+ }
+
+ thresh = 0;
+ temp_cfg = temp_map[thresh];
+ for (i = THRESH_MAX; i >= THRESH_MIN; i--) {
+ if (temp >= temp_map[i]) {
+ thresh = i;
+ temp_cfg = temp_map[i];
+ break;
+ }
+ }
+
+ if (temp_cfg == chip->temp_thresh_map[trip])
+ return 0;
+
+ ret = qpnp_tm_read(chip, addr, &reg);
+ if (ret < 0) {
+ dev_err(chip->dev, "LITE_TEMP_CFG read failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ reg &= ~LITE_TEMP_CFG_THRESHOLD_MASK;
+ reg |= FIELD_PREP(LITE_TEMP_CFG_THRESHOLD_MASK, thresh);
+
+ ret = qpnp_tm_write(chip, addr, reg);
+ if (ret < 0) {
+ dev_err(chip->dev, "LITE_TEMP_CFG write failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ chip->temp_thresh_map[trip] = temp_cfg;
+
+ return 0;
+}
+
+static int qpnp_tm_lite_set_trip_temp(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip, int temp)
+{
+ unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
+ struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = qpnp_tm_lite_set_temp_thresh(chip, trip_index, temp);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static const struct thermal_zone_device_ops qpnp_tm_lite_sensor_ops = {
+ .get_temp = qpnp_tm_get_temp,
+ .set_trip_temp = qpnp_tm_lite_set_trip_temp,
+};
+
static irqreturn_t qpnp_tm_isr(int irq, void *data)
{
struct qpnp_tm_chip *chip = data;
@@ -291,49 +524,227 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
return IRQ_HANDLED;
}
-/*
- * This function initializes the internal temp value based on only the
- * current thermal stage and threshold. Setup threshold control and
- * disable shutdown override.
- */
-static int qpnp_tm_init(struct qpnp_tm_chip *chip)
+/* Read the hardware default stage threshold temperatures */
+static int qpnp_tm_sync_thresholds(struct qpnp_tm_chip *chip)
{
- unsigned int stage;
+ u8 reg, threshold;
int ret;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg);
+ if (ret < 0)
+ return ret;
+
+ threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
+ sizeof(chip->temp_thresh_map));
+
+ return ret;
+}
+
+static int qpnp_tm_configure_trip_temp(struct qpnp_tm_chip *chip)
+{
+ int crit_temp, ret;
+
+ ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp);
+ if (ret)
+ crit_temp = THERMAL_TEMP_INVALID;
+
+ mutex_lock(&chip->lock);
+ ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+/* Configure TEMP_DAC registers based on DT thermal_zone trips */
+static int qpnp_tm_gen2_rev2_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
+{
+ struct qpnp_tm_chip *chip = data;
+ int ret;
+
+ mutex_lock(&chip->lock);
+ trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
+ ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, chip->ntrips, trip->temperature);
+ chip->ntrips++;
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int qpnp_tm_gen2_rev2_configure_trip_temps(struct qpnp_tm_chip *chip)
+{
+ int ret, i;
+
+ ret = thermal_zone_for_each_trip(chip->tz_dev,
+ qpnp_tm_gen2_rev2_configure_trip_temps_cb, chip);
+ if (ret < 0)
+ return ret;
+
+ /* Verify that trips are strictly increasing. */
+ for (i = 1; i < STAGE_COUNT; i++) {
+ if (chip->temp_thresh_map[i] <= chip->temp_thresh_map[i - 1]) {
+ dev_err(chip->dev, "Threshold %d=%ld <= threshold %d=%ld\n",
+ i, chip->temp_thresh_map[i], i - 1,
+ chip->temp_thresh_map[i - 1]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Read the hardware default TEMP_DAC stage threshold temperatures */
+static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip)
+{
+ int ret, i;
u8 reg = 0;
- int crit_temp;
+
+ for (i = 0; i < STAGE_COUNT; i++) {
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_TEMP_DAC_STG1 + i, &reg);
+ if (ret < 0)
+ return ret;
+
+ chip->temp_thresh_map[i] = TEMP_DAC_REG_TO_TEMP(reg);
+ }
+
+ return 0;
+}
+
+/* Configure TEMP_LITE registers based on DT thermal_zone trips */
+static int qpnp_tm_lite_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
+{
+ struct qpnp_tm_chip *chip = data;
+ int ret;
mutex_lock(&chip->lock);
+ trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
+ ret = qpnp_tm_lite_set_temp_thresh(chip, chip->ntrips, trip->temperature);
+ chip->ntrips++;
+ mutex_unlock(&chip->lock);
- ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg);
+ return ret;
+}
+
+static int qpnp_tm_lite_configure_trip_temps(struct qpnp_tm_chip *chip)
+{
+ int ret;
+
+ ret = thermal_zone_for_each_trip(chip->tz_dev, qpnp_tm_lite_configure_trip_temps_cb, chip);
+ if (ret < 0)
+ return ret;
+
+ /* Verify that trips are strictly increasing. */
+ if (chip->temp_thresh_map[2] <= chip->temp_thresh_map[0]) {
+ dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
+ chip->temp_thresh_map[2], chip->temp_thresh_map[0]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Read the hardware default TEMP_LITE stage threshold temperatures */
+static int qpnp_tm_lite_sync_thresholds(struct qpnp_tm_chip *chip)
+{
+ int ret, thresh;
+ u8 reg = 0;
+
+ /*
+ * Store the warning trip temp in temp_thresh_map[0] and the shutdown trip
+ * temp in temp_thresh_map[2]. The second trip point is purely in software
+ * to facilitate a controlled shutdown after the warning threshold is
+ * crossed but before the automatic hardware shutdown threshold is
+ * crossed. Thus, there is no register to read for the second trip
+ * point.
+ */
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG1, &reg);
if (ret < 0)
- goto out;
+ return ret;
- chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
- chip->temp = DEFAULT_TEMP;
+ thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
+ chip->temp_thresh_map[0] = temp_lite_warning_map[thresh];
- ret = qpnp_tm_get_temp_stage(chip);
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG2, &reg);
if (ret < 0)
- goto out;
- chip->stage = ret;
+ return ret;
- stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
- ? chip->stage : alarm_state_map[chip->stage];
+ thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
+ chip->temp_thresh_map[2] = temp_lite_shutdown_map[thresh];
- if (stage)
- chip->temp = qpnp_tm_decode_temp(chip, stage);
+ return 0;
+}
- mutex_unlock(&chip->lock);
+static const struct spmi_temp_alarm_data spmi_temp_alarm_data = {
+ .ops = &qpnp_tm_sensor_ops,
+ .temp_map = &temp_map_gen1,
+ .sync_thresholds = qpnp_tm_sync_thresholds,
+ .configure_trip_temps = qpnp_tm_configure_trip_temp,
+ .get_temp_stage = qpnp_tm_gen1_get_temp_stage,
+};
- ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp);
- if (ret)
- crit_temp = THERMAL_TEMP_INVALID;
+static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_data = {
+ .ops = &qpnp_tm_sensor_ops,
+ .temp_map = &temp_map_gen1,
+ .sync_thresholds = qpnp_tm_sync_thresholds,
+ .configure_trip_temps = qpnp_tm_configure_trip_temp,
+ .get_temp_stage = qpnp_tm_gen2_get_temp_stage,
+};
- mutex_lock(&chip->lock);
+static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev1_data = {
+ .ops = &qpnp_tm_sensor_ops,
+ .temp_map = &temp_map_gen2_v1,
+ .sync_thresholds = qpnp_tm_sync_thresholds,
+ .configure_trip_temps = qpnp_tm_configure_trip_temp,
+ .get_temp_stage = qpnp_tm_gen2_get_temp_stage,
+};
- ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
+static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = {
+ .ops = &qpnp_tm_gen2_rev2_sensor_ops,
+ .sync_thresholds = qpnp_tm_gen2_rev2_sync_thresholds,
+ .configure_trip_temps = qpnp_tm_gen2_rev2_configure_trip_temps,
+ .get_temp_stage = qpnp_tm_gen2_get_temp_stage,
+};
+
+static const struct spmi_temp_alarm_data spmi_temp_alarm_lite_data = {
+ .ops = &qpnp_tm_lite_sensor_ops,
+ .sync_thresholds = qpnp_tm_lite_sync_thresholds,
+ .configure_trip_temps = qpnp_tm_lite_configure_trip_temps,
+ .get_temp_stage = qpnp_tm_lite_get_temp_stage,
+};
+
+/*
+ * This function initializes the internal temp value based on only the
+ * current thermal stage and threshold.
+ */
+static int qpnp_tm_threshold_init(struct qpnp_tm_chip *chip)
+{
+ int ret;
+
+ ret = chip->data->sync_thresholds(chip);
+ if (ret < 0)
+ return ret;
+
+ ret = chip->data->get_temp_stage(chip);
+ if (ret < 0)
+ return ret;
+ chip->stage = ret;
+ chip->temp = DEFAULT_TEMP;
+
+ if (chip->stage)
+ chip->temp = qpnp_tm_decode_temp(chip, chip->stage);
+
+ return ret;
+}
+
+/* This function initializes threshold control and disables shutdown override. */
+static int qpnp_tm_init(struct qpnp_tm_chip *chip)
+{
+ int ret;
+ u8 reg;
+
+ ret = chip->data->configure_trip_temps(chip);
if (ret < 0)
- goto out;
+ return ret;
/* Enable the thermal alarm PMIC module in always-on mode. */
reg = ALARM_CTRL_FORCE_ENABLE;
@@ -341,8 +752,6 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
chip->initialized = true;
-out:
- mutex_unlock(&chip->lock);
return ret;
}
@@ -350,8 +759,8 @@ static int qpnp_tm_probe(struct platform_device *pdev)
{
struct qpnp_tm_chip *chip;
struct device_node *node;
- u8 type, subtype, dig_major;
- u32 res;
+ u8 type, subtype, dig_major, dig_minor;
+ u32 res, dig_revision;
int ret, irq;
node = pdev->dev.of_node;
@@ -360,7 +769,6 @@ static int qpnp_tm_probe(struct platform_device *pdev)
if (!chip)
return -ENOMEM;
- dev_set_drvdata(&pdev->dev, chip);
chip->dev = &pdev->dev;
mutex_init(&chip->lock);
@@ -403,18 +811,53 @@ static int qpnp_tm_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"could not read dig_major\n");
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MINOR, &dig_minor);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "could not read dig_minor\n");
+
if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
- && subtype != QPNP_TM_SUBTYPE_GEN2)) {
+ && subtype != QPNP_TM_SUBTYPE_GEN2
+ && subtype != QPNP_TM_SUBTYPE_LITE)) {
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
type, subtype);
return -ENODEV;
}
chip->subtype = subtype;
- if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
- chip->temp_map = &temp_map_gen2_v1;
+ if (subtype == QPNP_TM_SUBTYPE_GEN1)
+ chip->data = &spmi_temp_alarm_data;
+ else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 0)
+ chip->data = &spmi_temp_alarm_gen2_data;
+ else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 1)
+ chip->data = &spmi_temp_alarm_gen2_rev1_data;
+ else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2)
+ chip->data = &spmi_temp_alarm_gen2_rev2_data;
+ else if (subtype == QPNP_TM_SUBTYPE_LITE)
+ chip->data = &spmi_temp_alarm_lite_data;
else
- chip->temp_map = &temp_map_gen1;
+ return -ENODEV;
+
+ if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) {
+ dig_revision = (dig_major << 8) | dig_minor;
+ /*
+ * Check if stage 2 automatic partial shutdown must remain
+ * enabled to avoid potential repeated faults upon reaching
+ * over-temperature stage 3.
+ */
+ switch (dig_revision) {
+ case 0x0001:
+ case 0x0002:
+ case 0x0100:
+ case 0x0101:
+ chip->require_stage2_shutdown = true;
+ break;
+ }
+ }
+
+ ret = qpnp_tm_threshold_init(chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "threshold init failed\n");
/*
* Register the sensor before initializing the hardware to be able to
@@ -422,7 +865,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
* before the hardware initialization is completed.
*/
chip->tz_dev = devm_thermal_of_zone_register(
- &pdev->dev, 0, chip, &qpnp_tm_sensor_ops);
+ &pdev->dev, 0, chip, chip->data->ops);
if (IS_ERR(chip->tz_dev))
return dev_err_probe(&pdev->dev, PTR_ERR(chip->tz_dev),
"failed to register sensor\n");
diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c
index 1a7874676f68..faa5d00788ca 100644
--- a/drivers/thermal/qcom/tsens-v1.c
+++ b/drivers/thermal/qcom/tsens-v1.c
@@ -79,6 +79,17 @@ static struct tsens_features tsens_v1_feat = {
.trip_max_temp = 120000,
};
+static struct tsens_features tsens_v1_no_rpm_feat = {
+ .ver_major = VER_1_X_NO_RPM,
+ .crit_int = 0,
+ .combo_int = 0,
+ .adc = 1,
+ .srot_split = 1,
+ .max_sensors = 11,
+ .trip_min_temp = -40000,
+ .trip_max_temp = 120000,
+};
+
static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* VERSION */
@@ -150,6 +161,43 @@ static int __init init_8956(struct tsens_priv *priv) {
return init_common(priv);
}
+static int __init init_tsens_v1_no_rpm(struct tsens_priv *priv)
+{
+ int i, ret;
+ u32 mask = 0;
+
+ ret = init_common(priv);
+ if (ret < 0) {
+ dev_err(priv->dev, "Init common failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 1);
+ if (ret) {
+ dev_err(priv->dev, "Reset failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < priv->num_sensors; i++)
+ mask |= BIT(priv->sensor[i].hw_id);
+
+ ret = regmap_field_update_bits(priv->rf[SENSOR_EN], mask, mask);
+ if (ret) {
+ dev_err(priv->dev, "Sensor Enable failed\n");
+ return ret;
+ }
+
+ ret = regmap_field_write(priv->rf[TSENS_EN], 1);
+ if (ret) {
+ dev_err(priv->dev, "Enable failed\n");
+ return ret;
+ }
+
+ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 0);
+
+ return ret;
+}
+
static const struct tsens_ops ops_generic_v1 = {
.init = init_common,
.calibrate = calibrate_v1,
@@ -194,3 +242,17 @@ struct tsens_plat_data data_8976 = {
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
+
+static const struct tsens_ops ops_ipq5018 = {
+ .init = init_tsens_v1_no_rpm,
+ .calibrate = tsens_calibrate_common,
+ .get_temp = get_temp_tsens_valid,
+};
+
+const struct tsens_plat_data data_ipq5018 = {
+ .num_sensors = 5,
+ .ops = &ops_ipq5018,
+ .hw_ids = (unsigned int []){0, 1, 2, 3, 4},
+ .feat = &tsens_v1_no_rpm_feat,
+ .fields = tsens_v1_regfields,
+};
diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index 0cb7301eca6e..8d9698ea3ec4 100644
--- a/drivers/thermal/qcom/tsens-v2.c
+++ b/drivers/thermal/qcom/tsens-v2.c
@@ -4,13 +4,32 @@
* Copyright (c) 2018, Linaro Limited
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/nvmem-consumer.h>
#include <linux/regmap.h>
#include "tsens.h"
/* ----- SROT ------ */
#define SROT_HW_VER_OFF 0x0000
#define SROT_CTRL_OFF 0x0004
+#define SROT_MEASURE_PERIOD 0x0008
+#define SROT_Sn_CONVERSION 0x0060
+#define V2_SHIFT_DEFAULT 0x0003
+#define V2_SLOPE_DEFAULT 0x0cd0
+#define V2_CZERO_DEFAULT 0x016a
+#define ONE_PT_SLOPE 0x0cd0
+#define TWO_PT_SHIFTED_GAIN 921600
+#define ONE_PT_CZERO_CONST 94
+#define SW_RST_DEASSERT 0x0
+#define SW_RST_ASSERT 0x1
+#define MEASURE_PERIOD_2mSEC 0x1
+#define RESULT_FORMAT_TEMP 0x1
+#define TSENS_ENABLE 0x1
+#define SENSOR_CONVERSION(n) (((n) * 4) + SROT_Sn_CONVERSION)
+#define CONVERSION_SHIFT_MASK GENMASK(24, 23)
+#define CONVERSION_SLOPE_MASK GENMASK(22, 10)
+#define CONVERSION_CZERO_MASK GENMASK(9, 0)
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0004
@@ -50,6 +69,17 @@ static struct tsens_features ipq8074_feat = {
.trip_max_temp = 204000,
};
+static struct tsens_features ipq5332_feat = {
+ .ver_major = VER_2_X_NO_RPM,
+ .crit_int = 1,
+ .combo_int = 1,
+ .adc = 0,
+ .srot_split = 1,
+ .max_sensors = 16,
+ .trip_min_temp = 0,
+ .trip_max_temp = 204000,
+};
+
static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* VERSION */
@@ -59,6 +89,10 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
/* CTRL_OFF */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
+ [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 18),
+ [CODE_OR_TEMP] = REG_FIELD(SROT_CTRL_OFF, 21, 21),
+
+ [MAIN_MEASURE_PERIOD] = REG_FIELD(SROT_MEASURE_PERIOD, 0, 7),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
@@ -104,6 +138,128 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
+static int tsens_v2_calibrate_sensor(struct device *dev, struct tsens_sensor *sensor,
+ struct regmap *map, u32 mode, u32 base0, u32 base1)
+{
+ u32 shift = V2_SHIFT_DEFAULT;
+ u32 slope = V2_SLOPE_DEFAULT;
+ u32 czero = V2_CZERO_DEFAULT;
+ char name[20];
+ u32 val;
+ int ret;
+
+ /* Read offset value */
+ ret = snprintf(name, sizeof(name), "tsens_sens%d_off", sensor->hw_id);
+ if (ret < 0)
+ return ret;
+
+ ret = nvmem_cell_read_variable_le_u32(dev, name, &sensor->offset);
+ if (ret)
+ return ret;
+
+ /* Based on calib mode, program SHIFT, SLOPE and CZERO */
+ switch (mode) {
+ case TWO_PT_CALIB:
+ slope = (TWO_PT_SHIFTED_GAIN / (base1 - base0));
+
+ czero = (base0 + sensor->offset - ((base1 - base0) / 3));
+
+ break;
+ case ONE_PT_CALIB2:
+ czero = base0 + sensor->offset - ONE_PT_CZERO_CONST;
+
+ slope = ONE_PT_SLOPE;
+
+ break;
+ default:
+ dev_dbg(dev, "calibrationless mode\n");
+ }
+
+ val = FIELD_PREP(CONVERSION_SHIFT_MASK, shift) |
+ FIELD_PREP(CONVERSION_SLOPE_MASK, slope) |
+ FIELD_PREP(CONVERSION_CZERO_MASK, czero);
+
+ regmap_write(map, SENSOR_CONVERSION(sensor->hw_id), val);
+
+ return 0;
+}
+
+static int tsens_v2_calibration(struct tsens_priv *priv)
+{
+ struct device *dev = priv->dev;
+ u32 mode, base0, base1;
+ int i, ret;
+
+ if (priv->num_sensors > MAX_SENSORS)
+ return -EINVAL;
+
+ ret = nvmem_cell_read_variable_le_u32(priv->dev, "mode", &mode);
+ if (ret == -ENOENT)
+ dev_warn(priv->dev, "Calibration data not present in DT\n");
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(priv->dev, "calibration mode is %d\n", mode);
+
+ ret = nvmem_cell_read_variable_le_u32(priv->dev, "base0", &base0);
+ if (ret < 0)
+ return ret;
+
+ ret = nvmem_cell_read_variable_le_u32(priv->dev, "base1", &base1);
+ if (ret < 0)
+ return ret;
+
+ /* Calibrate each sensor */
+ for (i = 0; i < priv->num_sensors; i++) {
+ ret = tsens_v2_calibrate_sensor(dev, &priv->sensor[i], priv->srot_map,
+ mode, base0, base1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init init_tsens_v2_no_rpm(struct tsens_priv *priv)
+{
+ struct device *dev = priv->dev;
+ int i, ret;
+ u32 val = 0;
+
+ ret = init_common(priv);
+ if (ret < 0)
+ return ret;
+
+ priv->rf[CODE_OR_TEMP] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[CODE_OR_TEMP]);
+ if (IS_ERR(priv->rf[CODE_OR_TEMP]))
+ return PTR_ERR(priv->rf[CODE_OR_TEMP]);
+
+ priv->rf[MAIN_MEASURE_PERIOD] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[MAIN_MEASURE_PERIOD]);
+ if (IS_ERR(priv->rf[MAIN_MEASURE_PERIOD]))
+ return PTR_ERR(priv->rf[MAIN_MEASURE_PERIOD]);
+
+ regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_ASSERT);
+
+ regmap_field_write(priv->rf[MAIN_MEASURE_PERIOD], MEASURE_PERIOD_2mSEC);
+
+ /* Enable available sensors */
+ for (i = 0; i < priv->num_sensors; i++)
+ val |= 1 << priv->sensor[i].hw_id;
+
+ regmap_field_write(priv->rf[SENSOR_EN], val);
+
+ /* Select temperature format, unit is deci-Celsius */
+ regmap_field_write(priv->rf[CODE_OR_TEMP], RESULT_FORMAT_TEMP);
+
+ regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_DEASSERT);
+
+ regmap_field_write(priv->rf[TSENS_EN], TSENS_ENABLE);
+
+ return 0;
+}
+
static const struct tsens_ops ops_generic_v2 = {
.init = init_common,
.get_temp = get_temp_tsens_valid,
@@ -122,6 +278,28 @@ struct tsens_plat_data data_ipq8074 = {
.fields = tsens_v2_regfields,
};
+static const struct tsens_ops ops_ipq5332 = {
+ .init = init_tsens_v2_no_rpm,
+ .get_temp = get_temp_tsens_valid,
+ .calibrate = tsens_v2_calibration,
+};
+
+const struct tsens_plat_data data_ipq5332 = {
+ .num_sensors = 5,
+ .ops = &ops_ipq5332,
+ .hw_ids = (unsigned int []){11, 12, 13, 14, 15},
+ .feat = &ipq5332_feat,
+ .fields = tsens_v2_regfields,
+};
+
+const struct tsens_plat_data data_ipq5424 = {
+ .num_sensors = 7,
+ .ops = &ops_ipq5332,
+ .hw_ids = (unsigned int []){9, 10, 11, 12, 13, 14, 15},
+ .feat = &ipq5332_feat,
+ .fields = tsens_v2_regfields,
+};
+
/* Kept around for backward compatibility with old msm8996.dtsi */
struct tsens_plat_data data_8996 = {
.num_sensors = 13,
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index 3aa3736181aa..a2422ebee816 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -447,7 +447,7 @@ static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
enable ? "en" : "dis");
- if (tsens_version(priv) > VER_1_X)
+ if (tsens_version(priv) >= VER_2_X)
tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
else
tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
@@ -499,7 +499,7 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
if (ret)
return ret;
- if (tsens_version(priv) > VER_1_X) {
+ if (tsens_version(priv) >= VER_2_X) {
ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
if (ret)
return ret;
@@ -543,7 +543,7 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
{
- if (ver > VER_1_X)
+ if (ver >= VER_2_X)
return mask & (1 << hw_id);
/* v1, v0.1 don't have a irq mask register */
@@ -733,7 +733,7 @@ static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high)
static int tsens_enable_irq(struct tsens_priv *priv)
{
int ret;
- int val = tsens_version(priv) > VER_1_X ? 7 : 1;
+ int val = tsens_version(priv) >= VER_2_X ? 7 : 1;
ret = regmap_field_write(priv->rf[INT_EN], val);
if (ret < 0)
@@ -976,9 +976,15 @@ int __init init_common(struct tsens_priv *priv)
if (ret)
goto err_put_device;
if (!enabled) {
- dev_err(dev, "%s: device not enabled\n", __func__);
- ret = -ENODEV;
- goto err_put_device;
+ switch (tsens_version(priv)) {
+ case VER_1_X_NO_RPM:
+ case VER_2_X_NO_RPM:
+ break;
+ default:
+ dev_err(dev, "%s: device not enabled\n", __func__);
+ ret = -ENODEV;
+ goto err_put_device;
+ }
}
priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
@@ -1040,7 +1046,7 @@ int __init init_common(struct tsens_priv *priv)
}
}
- if (tsens_version(priv) > VER_1_X && ver_minor > 2) {
+ if (tsens_version(priv) >= VER_2_X && ver_minor > 2) {
/* Watchdog is present only on v2.3+ */
priv->feat->has_watchdog = 1;
for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
@@ -1102,6 +1108,15 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
static const struct of_device_id tsens_table[] = {
{
+ .compatible = "qcom,ipq5018-tsens",
+ .data = &data_ipq5018,
+ }, {
+ .compatible = "qcom,ipq5332-tsens",
+ .data = &data_ipq5332,
+ }, {
+ .compatible = "qcom,ipq5424-tsens",
+ .data = &data_ipq5424,
+ }, {
.compatible = "qcom,ipq8064-tsens",
.data = &data_8960,
}, {
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index 7b36a0318fa6..2a7afa4c899b 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -34,7 +34,9 @@ enum tsens_ver {
VER_0 = 0,
VER_0_1,
VER_1_X,
+ VER_1_X_NO_RPM,
VER_2_X,
+ VER_2_X_NO_RPM,
};
enum tsens_irq_type {
@@ -168,6 +170,7 @@ enum regfield_ids {
TSENS_SW_RST,
SENSOR_EN,
CODE_OR_TEMP,
+ MAIN_MEASURE_PERIOD,
/* ----- TM ------ */
/* TRDY */
@@ -649,7 +652,11 @@ extern struct tsens_plat_data data_8226, data_8909, data_8916, data_8939, data_8
/* TSENS v1 targets */
extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956;
+/* TSENS v1 with no RPM targets */
+extern const struct tsens_plat_data data_ipq5018;
+
/* TSENS v2 targets */
extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;
+extern const struct tsens_plat_data data_ipq5332, data_ipq5424;
#endif /* __QCOM_TSENS_H__ */
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
index 52e26be8c53d..01b58be0dcc6 100644
--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -18,6 +18,7 @@
#define SITES_MAX 16
#define TMR_DISABLE 0x0
#define TMR_ME 0x80000000
+#define TMR_CMD BIT(29)
#define TMR_ALPF 0x0c000000
#define TMR_ALPF_V2 0x03000000
#define TMTMIR_DEFAULT 0x0000000f
@@ -265,7 +266,6 @@ static void qoriq_tmu_action(void *p)
struct qoriq_tmu_data *data = p;
regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
- clk_disable_unprepare(data->clk);
}
static int qoriq_tmu_probe(struct platform_device *pdev)
@@ -296,38 +296,27 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
base = devm_platform_ioremap_resource(pdev, 0);
ret = PTR_ERR_OR_ZERO(base);
- if (ret) {
- dev_err(dev, "Failed to get memory region\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get memory region\n");
data->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
ret = PTR_ERR_OR_ZERO(data->regmap);
- if (ret) {
- dev_err(dev, "Failed to init regmap (%d)\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init regmap\n");
- data->clk = devm_clk_get_optional(dev, NULL);
+ data->clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(data->clk))
return PTR_ERR(data->clk);
- ret = clk_prepare_enable(data->clk);
- if (ret) {
- dev_err(dev, "Failed to enable clock\n");
- return ret;
- }
-
ret = devm_add_action_or_reset(dev, qoriq_tmu_action, data);
if (ret)
return ret;
/* version register offset at: 0xbf8 on both v1 and v2 */
ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver);
- if (ret) {
- dev_err(&pdev->dev, "Failed to read IP block version\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read IP block version\n");
+
data->ver = (ver >> 8) & 0xff;
qoriq_tmu_init_device(data); /* TMU initialization */
@@ -337,10 +326,8 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
return ret;
ret = qoriq_tmu_register_tmu_zone(dev, data);
- if (ret < 0) {
- dev_err(dev, "Failed to register sensors\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to register sensors\n");
platform_set_drvdata(pdev, data);
@@ -356,6 +343,12 @@ static int qoriq_tmu_suspend(struct device *dev)
if (ret)
return ret;
+ if (data->ver > TMU_VER1) {
+ ret = regmap_set_bits(data->regmap, REGS_TMR, TMR_CMD);
+ if (ret)
+ return ret;
+ }
+
clk_disable_unprepare(data->clk);
return 0;
@@ -370,6 +363,12 @@ static int qoriq_tmu_resume(struct device *dev)
if (ret)
return ret;
+ if (data->ver > TMU_VER1) {
+ ret = regmap_clear_bits(data->regmap, REGS_TMR, TMR_CMD);
+ if (ret)
+ return ret;
+ }
+
/* Enable monitoring */
return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
}
diff --git a/drivers/thermal/renesas/Kconfig b/drivers/thermal/renesas/Kconfig
index dcf5fc5ae08e..5735c8728a31 100644
--- a/drivers/thermal/renesas/Kconfig
+++ b/drivers/thermal/renesas/Kconfig
@@ -10,13 +10,13 @@ config RCAR_THERMAL
thermal framework.
config RCAR_GEN3_THERMAL
- tristate "Renesas R-Car Gen3 and RZ/G2 thermal driver"
+ tristate "Renesas R-Car Gen3/Gen4 and RZ/G2 thermal driver"
depends on ARCH_RENESAS || COMPILE_TEST
depends on HAS_IOMEM
depends on OF
help
- Enable this to plug the R-Car Gen3 or RZ/G2 thermal sensor driver into
- the Linux thermal framework.
+ Enable this to plug the R-Car Gen3/Gen4 or RZ/G2 thermal sensor
+ driver into the Linux thermal framework.
config RZG2L_THERMAL
tristate "Renesas RZ/G2L thermal driver"
@@ -26,3 +26,18 @@ config RZG2L_THERMAL
help
Enable this to plug the RZ/G2L thermal sensor driver into the Linux
thermal framework.
+
+config RZG3E_THERMAL
+ tristate "Renesas RZ/G3E thermal driver"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ help
+ Enable this to plug the RZ/G3E thermal sensor driver into the Linux
+ thermal framework.
+
+config RZG3S_THERMAL
+ tristate "Renesas RZ/G3S thermal driver"
+ depends on ARCH_R9A08G045 || COMPILE_TEST
+ depends on OF && IIO && RZG2L_ADC
+ help
+ Enable this to plug the RZ/G3S thermal sensor driver into the Linux
+ thermal framework.
diff --git a/drivers/thermal/renesas/Makefile b/drivers/thermal/renesas/Makefile
index bf9cb3cb94d6..8f5ae9af277c 100644
--- a/drivers/thermal/renesas/Makefile
+++ b/drivers/thermal/renesas/Makefile
@@ -3,3 +3,5 @@
obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_RZG2L_THERMAL) += rzg2l_thermal.o
+obj-$(CONFIG_RZG3E_THERMAL) += rzg3e_thermal.o
+obj-$(CONFIG_RZG3S_THERMAL) += rzg3s_thermal.o
diff --git a/drivers/thermal/renesas/rcar_gen3_thermal.c b/drivers/thermal/renesas/rcar_gen3_thermal.c
index 1ec169aeacfc..94804816e9e1 100644
--- a/drivers/thermal/renesas/rcar_gen3_thermal.c
+++ b/drivers/thermal/renesas/rcar_gen3_thermal.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * R-Car Gen3 THS thermal sensor driver
+ * R-Car Gen3, Gen4 and RZ/G2 THS thermal sensor driver
* Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen.
*
* Copyright (C) 2016 Renesas Electronics Corporation.
@@ -21,11 +21,11 @@
/* Register offsets */
#define REG_GEN3_IRQSTR 0x04
#define REG_GEN3_IRQMSK 0x08
-#define REG_GEN3_IRQCTL 0x0C
+#define REG_GEN3_IRQCTL 0x0c
#define REG_GEN3_IRQEN 0x10
#define REG_GEN3_IRQTEMP1 0x14
#define REG_GEN3_IRQTEMP2 0x18
-#define REG_GEN3_IRQTEMP3 0x1C
+#define REG_GEN3_IRQTEMP3 0x1c
#define REG_GEN3_THCTR 0x20
#define REG_GEN3_TEMP 0x28
#define REG_GEN3_THCODE1 0x50
@@ -38,9 +38,9 @@
#define REG_GEN4_THSFMON00 0x180
#define REG_GEN4_THSFMON01 0x184
#define REG_GEN4_THSFMON02 0x188
-#define REG_GEN4_THSFMON15 0x1BC
-#define REG_GEN4_THSFMON16 0x1C0
-#define REG_GEN4_THSFMON17 0x1C4
+#define REG_GEN4_THSFMON15 0x1bc
+#define REG_GEN4_THSFMON16 0x1c0
+#define REG_GEN4_THSFMON17 0x1c4
/* IRQ{STR,MSK,EN} bits */
#define IRQ_TEMP1 BIT(0)
@@ -57,21 +57,33 @@
/* THSCP bits */
#define THSCP_COR_PARA_VLD (BIT(15) | BIT(14))
-#define CTEMP_MASK 0xFFF
+#define CTEMP_MASK 0xfff
#define MCELSIUS(temp) ((temp) * 1000)
-#define GEN3_FUSE_MASK 0xFFF
-#define GEN4_FUSE_MASK 0xFFF
+#define GEN3_FUSE_MASK 0xfff
+#define GEN4_FUSE_MASK 0xfff
#define TSC_MAX_NUM 5
struct rcar_gen3_thermal_priv;
+struct rcar_gen3_thermal_fuse_info {
+ u32 ptat[3];
+ u32 thcode[3];
+ u32 mask;
+};
+
+struct rcar_gen3_thermal_fuse_default {
+ u32 ptat[3];
+ u32 thcodes[TSC_MAX_NUM][3];
+};
+
struct rcar_thermal_info {
int scale;
int adj_below;
int adj_above;
- void (*read_fuses)(struct rcar_gen3_thermal_priv *priv);
+ const struct rcar_gen3_thermal_fuse_info *fuses;
+ const struct rcar_gen3_thermal_fuse_default *fuse_defaults;
};
struct equation_set_coef {
@@ -159,7 +171,7 @@ static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
const struct equation_set_coef *coef;
int adj, decicelsius, reg, thcode;
- /* Read register and convert to mili Celsius */
+ /* Read register and convert to millidegree Celsius */
reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;
if (reg < tsc->thcode[1]) {
@@ -253,96 +265,62 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static void rcar_gen3_thermal_read_fuses_gen3(struct rcar_gen3_thermal_priv *priv)
+static void rcar_gen3_thermal_fetch_fuses(struct rcar_gen3_thermal_priv *priv)
{
- unsigned int i;
+ const struct rcar_gen3_thermal_fuse_info *fuses = priv->info->fuses;
/*
* Set the pseudo calibration points with fused values.
* PTAT is shared between all TSCs but only fused for the first
* TSC while THCODEs are fused for each TSC.
*/
- priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT1) &
- GEN3_FUSE_MASK;
- priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT2) &
- GEN3_FUSE_MASK;
- priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT3) &
- GEN3_FUSE_MASK;
-
- for (i = 0; i < priv->num_tscs; i++) {
+ priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], fuses->ptat[0])
+ & fuses->mask;
+ priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], fuses->ptat[1])
+ & fuses->mask;
+ priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], fuses->ptat[2])
+ & fuses->mask;
+
+ for (unsigned int i = 0; i < priv->num_tscs; i++) {
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
- tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE1) &
- GEN3_FUSE_MASK;
- tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE2) &
- GEN3_FUSE_MASK;
- tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE3) &
- GEN3_FUSE_MASK;
- }
-}
-
-static void rcar_gen3_thermal_read_fuses_gen4(struct rcar_gen3_thermal_priv *priv)
-{
- unsigned int i;
-
- /*
- * Set the pseudo calibration points with fused values.
- * PTAT is shared between all TSCs but only fused for the first
- * TSC while THCODEs are fused for each TSC.
- */
- priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON16) &
- GEN4_FUSE_MASK;
- priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON17) &
- GEN4_FUSE_MASK;
- priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON15) &
- GEN4_FUSE_MASK;
-
- for (i = 0; i < priv->num_tscs; i++) {
- struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
-
- tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON01) &
- GEN4_FUSE_MASK;
- tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON02) &
- GEN4_FUSE_MASK;
- tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON00) &
- GEN4_FUSE_MASK;
+ tsc->thcode[0] = rcar_gen3_thermal_read(tsc, fuses->thcode[0])
+ & fuses->mask;
+ tsc->thcode[1] = rcar_gen3_thermal_read(tsc, fuses->thcode[1])
+ & fuses->mask;
+ tsc->thcode[2] = rcar_gen3_thermal_read(tsc, fuses->thcode[2])
+ & fuses->mask;
}
}
static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)
{
+ const struct rcar_gen3_thermal_fuse_default *fuse_defaults = priv->info->fuse_defaults;
unsigned int i;
u32 thscp;
/* If fuses are not set, fallback to pseudo values. */
thscp = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_THSCP);
- if (!priv->info->read_fuses ||
+ if (!priv->info->fuses ||
(thscp & THSCP_COR_PARA_VLD) != THSCP_COR_PARA_VLD) {
/* Default THCODE values in case FUSEs are not set. */
- static const int thcodes[TSC_MAX_NUM][3] = {
- { 3397, 2800, 2221 },
- { 3393, 2795, 2216 },
- { 3389, 2805, 2237 },
- { 3415, 2694, 2195 },
- { 3356, 2724, 2244 },
- };
-
- priv->ptat[0] = 2631;
- priv->ptat[1] = 1509;
- priv->ptat[2] = 435;
+ priv->ptat[0] = fuse_defaults->ptat[0];
+ priv->ptat[1] = fuse_defaults->ptat[1];
+ priv->ptat[2] = fuse_defaults->ptat[2];
for (i = 0; i < priv->num_tscs; i++) {
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
- tsc->thcode[0] = thcodes[i][0];
- tsc->thcode[1] = thcodes[i][1];
- tsc->thcode[2] = thcodes[i][2];
+ tsc->thcode[0] = fuse_defaults->thcodes[i][0];
+ tsc->thcode[1] = fuse_defaults->thcodes[i][1];
+ tsc->thcode[2] = fuse_defaults->thcodes[i][2];
}
return false;
}
- priv->info->read_fuses(priv);
+ rcar_gen3_thermal_fetch_fuses(priv);
+
return true;
}
@@ -370,25 +348,69 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv,
usleep_range(1000, 2000);
}
+static const struct rcar_gen3_thermal_fuse_info rcar_gen3_thermal_fuse_info_gen3 = {
+ .ptat = { REG_GEN3_PTAT1, REG_GEN3_PTAT2, REG_GEN3_PTAT3 },
+ .thcode = { REG_GEN3_THCODE1, REG_GEN3_THCODE2, REG_GEN3_THCODE3 },
+ .mask = GEN3_FUSE_MASK,
+};
+
+static const struct rcar_gen3_thermal_fuse_info rcar_gen3_thermal_fuse_info_gen4 = {
+ .ptat = { REG_GEN4_THSFMON16, REG_GEN4_THSFMON17, REG_GEN4_THSFMON15 },
+ .thcode = { REG_GEN4_THSFMON01, REG_GEN4_THSFMON02, REG_GEN4_THSFMON00 },
+ .mask = GEN4_FUSE_MASK,
+};
+
+static const struct rcar_gen3_thermal_fuse_default rcar_gen3_thermal_fuse_default_info_gen3 = {
+ .ptat = { 2631, 1509, 435 },
+ .thcodes = {
+ { 3397, 2800, 2221 },
+ { 3393, 2795, 2216 },
+ { 3389, 2805, 2237 },
+ { 3415, 2694, 2195 },
+ { 3356, 2724, 2244 },
+ },
+};
+
+static const struct rcar_gen3_thermal_fuse_default rcar_gen3_thermal_fuse_default_info_gen4 = {
+ .ptat = { 3274, 2164, 985 },
+ .thcodes = { /* All four THS units share the same trimming */
+ { 3218, 2617, 1980 },
+ { 3218, 2617, 1980 },
+ { 3218, 2617, 1980 },
+ { 3218, 2617, 1980 },
+ }
+};
+
static const struct rcar_thermal_info rcar_m3w_thermal_info = {
.scale = 157,
.adj_below = -41,
.adj_above = 116,
- .read_fuses = rcar_gen3_thermal_read_fuses_gen3,
+ .fuses = &rcar_gen3_thermal_fuse_info_gen3,
+ .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3,
};
static const struct rcar_thermal_info rcar_gen3_thermal_info = {
.scale = 167,
.adj_below = -41,
.adj_above = 126,
- .read_fuses = rcar_gen3_thermal_read_fuses_gen3,
+ .fuses = &rcar_gen3_thermal_fuse_info_gen3,
+ .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3,
+};
+
+static const struct rcar_thermal_info rcar_s4_thermal_info = {
+ .scale = 167,
+ .adj_below = -41,
+ .adj_above = 126,
+ .fuses = &rcar_gen3_thermal_fuse_info_gen4,
+ .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3,
};
static const struct rcar_thermal_info rcar_gen4_thermal_info = {
.scale = 167,
.adj_below = -41,
.adj_above = 126,
- .read_fuses = rcar_gen3_thermal_read_fuses_gen4,
+ .fuses = &rcar_gen3_thermal_fuse_info_gen4,
+ .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen4,
};
static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
@@ -430,7 +452,7 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
},
{
.compatible = "renesas,r8a779f0-thermal",
- .data = &rcar_gen4_thermal_info,
+ .data = &rcar_s4_thermal_info,
},
{
.compatible = "renesas,r8a779g0-thermal",
@@ -579,7 +601,7 @@ error_unregister:
return ret;
}
-static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
+static int rcar_gen3_thermal_resume(struct device *dev)
{
struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
unsigned int i;
@@ -593,13 +615,13 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
- rcar_gen3_thermal_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
+ rcar_gen3_thermal_resume);
static struct platform_driver rcar_gen3_thermal_driver = {
.driver = {
.name = "rcar_gen3_thermal",
- .pm = &rcar_gen3_thermal_pm_ops,
+ .pm = pm_sleep_ptr(&rcar_gen3_thermal_pm_ops),
.of_match_table = rcar_gen3_thermal_dt_ids,
},
.probe = rcar_gen3_thermal_probe,
diff --git a/drivers/thermal/renesas/rcar_thermal.c b/drivers/thermal/renesas/rcar_thermal.c
index 00a66ee0a5b0..6e5dcac5d47a 100644
--- a/drivers/thermal/renesas/rcar_thermal.c
+++ b/drivers/thermal/renesas/rcar_thermal.c
@@ -277,7 +277,7 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp)
return rcar_thermal_get_current_temp(priv, temp);
}
-static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
+static const struct thermal_zone_device_ops rcar_thermal_zone_ops = {
.get_temp = rcar_thermal_get_temp,
};
@@ -534,7 +534,6 @@ error_unregister:
return ret;
}
-#ifdef CONFIG_PM_SLEEP
static int rcar_thermal_suspend(struct device *dev)
{
struct rcar_thermal_common *common = dev_get_drvdata(dev);
@@ -567,15 +566,14 @@ static int rcar_thermal_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend,
- rcar_thermal_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend,
+ rcar_thermal_resume);
static struct platform_driver rcar_thermal_driver = {
.driver = {
.name = "rcar_thermal",
- .pm = &rcar_thermal_pm_ops,
+ .pm = pm_sleep_ptr(&rcar_thermal_pm_ops),
.of_match_table = rcar_thermal_dt_ids,
},
.probe = rcar_thermal_probe,
diff --git a/drivers/thermal/renesas/rzg3e_thermal.c b/drivers/thermal/renesas/rzg3e_thermal.c
new file mode 100644
index 000000000000..f0e29fe633db
--- /dev/null
+++ b/drivers/thermal/renesas/rzg3e_thermal.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G3E TSU Temperature Sensor Unit
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/thermal.h>
+#include <linux/units.h>
+
+#include "../thermal_hwmon.h"
+
+/* TSU Register offsets and bits */
+#define TSU_SSUSR 0x00
+#define TSU_SSUSR_EN_TS BIT(0)
+#define TSU_SSUSR_ADC_PD_TS BIT(1)
+#define TSU_SSUSR_SOC_TS_EN BIT(2)
+
+#define TSU_STRGR 0x04
+#define TSU_STRGR_ADST BIT(0)
+
+#define TSU_SOSR1 0x08
+#define TSU_SOSR1_ADCT_8 0x03
+#define TSU_SOSR1_ADCS BIT(4)
+#define TSU_SOSR1_OUTSEL BIT(9)
+
+#define TSU_SCRR 0x10
+#define TSU_SCRR_OUT12BIT_TS GENMASK(11, 0)
+
+#define TSU_SSR 0x14
+#define TSU_SSR_CONV BIT(0)
+
+#define TSU_CMSR 0x18
+#define TSU_CMSR_CMPEN BIT(0)
+
+#define TSU_LLSR 0x1C
+#define TSU_ULSR 0x20
+
+#define TSU_SISR 0x30
+#define TSU_SISR_ADF BIT(0)
+#define TSU_SISR_CMPF BIT(1)
+
+#define TSU_SIER 0x34
+#define TSU_SIER_CMPIE BIT(1)
+
+#define TSU_SICR 0x38
+#define TSU_SICR_ADCLR BIT(0)
+#define TSU_SICR_CMPCLR BIT(1)
+
+/* Temperature calculation constants from datasheet */
+#define TSU_CODE_MAX 0xFFF
+
+/* Timing specifications from datasheet */
+#define TSU_POWERUP_TIME_US 120 /* 120T at 1MHz sensor clock per datasheet */
+#define TSU_CONV_TIME_US 50 /* Per sample conversion time */
+#define TSU_POLL_DELAY_US 10 /* Polling interval */
+#define TSU_MIN_CLOCK_RATE 24000000 /* TSU_PCLK minimum 24MHz */
+
+#define RZ_SIP_SVC_GET_SYSTSU 0x82000022
+#define OTP_TSU_REG_ADR_TEMPHI 0x01DC
+#define OTP_TSU_REG_ADR_TEMPLO 0x01DD
+
+struct rzg3e_thermal_priv;
+
+struct rzg3e_thermal_info {
+ int (*get_trim)(struct rzg3e_thermal_priv *priv);
+ int temp_d_mc;
+ int temp_e_mc;
+};
+
+/**
+ * struct rzg3e_thermal_priv - RZ/G3E TSU private data
+ * @base: TSU register base
+ * @dev: device pointer
+ * @syscon: regmap for calibration values
+ * @zone: thermal zone device
+ * @rstc: reset control
+ * @info: chip type specific information
+ * @trmval0: calibration value 0 (b)
+ * @trmval1: calibration value 1 (c)
+ * @lock: protects hardware access during conversions
+ */
+struct rzg3e_thermal_priv {
+ void __iomem *base;
+ struct device *dev;
+ struct thermal_zone_device *zone;
+ struct reset_control *rstc;
+ const struct rzg3e_thermal_info *info;
+ u16 trmval0;
+ u16 trmval1;
+ struct mutex lock;
+};
+
+static int rzg3e_thermal_power_on(struct rzg3e_thermal_priv *priv)
+{
+ u32 val;
+ int ret;
+
+ /* Clear any pending interrupts */
+ writel(TSU_SICR_ADCLR | TSU_SICR_CMPCLR, priv->base + TSU_SICR);
+
+ /* Disable all interrupts during setup */
+ writel(0, priv->base + TSU_SIER);
+
+ /*
+ * Power-on sequence per datasheet 7.11.9.1:
+ * SOC_TS_EN must be set at same time or before EN_TS and ADC_PD_TS
+ */
+ val = TSU_SSUSR_SOC_TS_EN | TSU_SSUSR_EN_TS;
+ writel(val, priv->base + TSU_SSUSR);
+
+ /* Wait for sensor stabilization per datasheet 7.11.7.1 */
+ usleep_range(TSU_POWERUP_TIME_US, TSU_POWERUP_TIME_US + 10);
+
+ /* Configure for average mode with 8 samples */
+ val = TSU_SOSR1_OUTSEL | TSU_SOSR1_ADCT_8;
+ writel(val, priv->base + TSU_SOSR1);
+
+ /* Ensure we're in single scan mode (default) */
+ val = readl(priv->base + TSU_SOSR1);
+ if (val & TSU_SOSR1_ADCS) {
+ dev_err(priv->dev, "Invalid scan mode setting\n");
+ return -EINVAL;
+ }
+
+ /* Wait for any ongoing conversion to complete */
+ ret = readl_poll_timeout(priv->base + TSU_SSR, val,
+ !(val & TSU_SSR_CONV),
+ TSU_POLL_DELAY_US,
+ USEC_PER_MSEC);
+ if (ret) {
+ dev_err(priv->dev, "Timeout waiting for conversion\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rzg3e_thermal_power_off(struct rzg3e_thermal_priv *priv)
+{
+ /* Disable all interrupts */
+ writel(0, priv->base + TSU_SIER);
+
+ /* Clear pending interrupts */
+ writel(TSU_SICR_ADCLR | TSU_SICR_CMPCLR, priv->base + TSU_SICR);
+
+ /* Power down sequence per datasheet */
+ writel(TSU_SSUSR_ADC_PD_TS, priv->base + TSU_SSUSR);
+}
+
+/*
+ * Convert 12-bit sensor code to temperature in millicelsius
+ * Formula from datasheet 7.11.7.8:
+ * T(°C) = ((e - d) / (c - b)) * (a - b) + d
+ * where: a = sensor code, b = trmval0, c = trmval1, d = -41, e = 126
+ */
+static int rzg3e_thermal_code_to_temp(struct rzg3e_thermal_priv *priv, u16 code)
+{
+ const struct rzg3e_thermal_info *info = priv->info;
+ s64 numerator, denominator;
+ int temp_mc;
+
+ numerator = (info->temp_e_mc - info->temp_d_mc) *
+ (s64)(code - priv->trmval0);
+ denominator = priv->trmval1 - priv->trmval0;
+
+ temp_mc = div64_s64(numerator, denominator) + info->temp_d_mc;
+
+ return clamp(temp_mc, info->temp_d_mc, info->temp_e_mc);
+}
+
+/*
+ * Convert temperature in millicelsius to 12-bit sensor code
+ * Formula from datasheet 7.11.7.9 (inverse of above)
+ */
+static u16 rzg3e_thermal_temp_to_code(struct rzg3e_thermal_priv *priv, int temp_mc)
+{
+ const struct rzg3e_thermal_info *info = priv->info;
+ s64 numerator, denominator;
+ s64 code;
+
+ numerator = (temp_mc - info->temp_d_mc) * (priv->trmval1 - priv->trmval0);
+ denominator = info->temp_e_mc - info->temp_d_mc;
+
+ code = div64_s64(numerator, denominator) + priv->trmval0;
+
+ return clamp_val(code, 0, TSU_CODE_MAX);
+}
+
+static int rzg3e_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct rzg3e_thermal_priv *priv = thermal_zone_device_priv(tz);
+ u32 status, code;
+ int ret, timeout;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&priv->lock);
+
+ /* Clear any previous conversion status */
+ writel(TSU_SICR_ADCLR, priv->base + TSU_SICR);
+
+ /* Start single conversion */
+ writel(TSU_STRGR_ADST, priv->base + TSU_STRGR);
+
+ /* Wait for conversion completion - 8 samples at ~50us each */
+ timeout = TSU_CONV_TIME_US * 8 * 2; /* Double for margin */
+ ret = readl_poll_timeout(priv->base + TSU_SISR, status,
+ status & TSU_SISR_ADF,
+ TSU_POLL_DELAY_US, timeout);
+ if (ret) {
+ dev_err(priv->dev, "Conversion timeout (status=0x%08x)\n", status);
+ goto out;
+ }
+
+ /* Read the averaged result and clear the complete flag */
+ code = readl(priv->base + TSU_SCRR) & TSU_SCRR_OUT12BIT_TS;
+ writel(TSU_SICR_ADCLR, priv->base + TSU_SICR);
+
+ /* Convert to temperature */
+ *temp = rzg3e_thermal_code_to_temp(priv, code);
+
+ dev_dbg(priv->dev, "temp=%d mC (%d.%03d°C), code=0x%03x\n",
+ *temp, *temp / 1000, abs(*temp) % 1000, code);
+
+out:
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+ return ret;
+}
+
+static int rzg3e_thermal_set_trips(struct thermal_zone_device *tz,
+ int low, int high)
+{
+ struct rzg3e_thermal_priv *priv = thermal_zone_device_priv(tz);
+ u16 low_code, high_code;
+ u32 val;
+ int ret;
+
+ /* Hardware requires low < high */
+ if (low >= high)
+ return -EINVAL;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&priv->lock);
+
+ /* Convert temperatures to codes */
+ low_code = rzg3e_thermal_temp_to_code(priv, low);
+ high_code = rzg3e_thermal_temp_to_code(priv, high);
+
+ dev_dbg(priv->dev, "set_trips: low=%d high=%d (codes: 0x%03x/0x%03x)\n",
+ low, high, low_code, high_code);
+
+ /* Disable comparison during reconfiguration */
+ writel(0, priv->base + TSU_SIER);
+ writel(0, priv->base + TSU_CMSR);
+
+ /* Clear any pending comparison interrupts */
+ writel(TSU_SICR_CMPCLR, priv->base + TSU_SICR);
+
+ /* Set trip points */
+ writel(low_code, priv->base + TSU_LLSR);
+ writel(high_code, priv->base + TSU_ULSR);
+
+ /*
+ * Ensure OUTSEL is set for comparison per datasheet 7.11.7.4
+ * Comparison uses averaged data
+ */
+ val = readl(priv->base + TSU_SOSR1);
+ val |= TSU_SOSR1_OUTSEL;
+ writel(val, priv->base + TSU_SOSR1);
+
+ /* Enable comparison with "out of range" mode (CMPCOND=0) */
+ writel(TSU_CMSR_CMPEN, priv->base + TSU_CMSR);
+
+ /* Unmask compare IRQ and start a conversion to evaluate window */
+ writel(TSU_SIER_CMPIE, priv->base + TSU_SIER);
+ writel(TSU_STRGR_ADST, priv->base + TSU_STRGR);
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return 0;
+}
+
+static irqreturn_t rzg3e_thermal_irq_thread(int irq, void *data)
+{
+ struct rzg3e_thermal_priv *priv = data;
+
+ dev_dbg(priv->dev, "Temperature threshold crossed\n");
+
+ /* Notify thermal framework to re-evaluate trip points */
+ thermal_zone_device_update(priv->zone, THERMAL_TRIP_VIOLATED);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rzg3e_thermal_irq(int irq, void *data)
+{
+ struct rzg3e_thermal_priv *priv = data;
+ u32 status;
+
+ status = readl(priv->base + TSU_SISR);
+
+ /* Check if comparison interrupt occurred */
+ if (status & TSU_SISR_CMPF) {
+ /* Clear irq flag and disable interrupt until reconfigured */
+ writel(TSU_SICR_CMPCLR, priv->base + TSU_SICR);
+ writel(0, priv->base + TSU_SIER);
+
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static const struct thermal_zone_device_ops rzg3e_tz_ops = {
+ .get_temp = rzg3e_thermal_get_temp,
+ .set_trips = rzg3e_thermal_set_trips,
+};
+
+static int rzg3e_thermal_get_syscon_trim(struct rzg3e_thermal_priv *priv)
+{
+ struct device_node *np = priv->dev->of_node;
+ struct regmap *syscon;
+ u32 offset;
+ int ret;
+ u32 val;
+
+ syscon = syscon_regmap_lookup_by_phandle_args(np, "renesas,tsu-trim", 1, &offset);
+ if (IS_ERR(syscon))
+ return dev_err_probe(priv->dev, PTR_ERR(syscon),
+ "Failed to parse renesas,tsu-trim\n");
+
+ /* Read calibration values from syscon */
+ ret = regmap_read(syscon, offset, &val);
+ if (ret)
+ return ret;
+ priv->trmval0 = val & TSU_CODE_MAX;
+
+ ret = regmap_read(syscon, offset + 4, &val);
+ if (ret)
+ return ret;
+ priv->trmval1 = val & TSU_CODE_MAX;
+
+ return 0;
+}
+
+static int rzg3e_thermal_get_smc_trim(struct rzg3e_thermal_priv *priv)
+{
+ struct arm_smccc_res local_res;
+
+ arm_smccc_smc(RZ_SIP_SVC_GET_SYSTSU, OTP_TSU_REG_ADR_TEMPLO,
+ 0, 0, 0, 0, 0, 0, &local_res);
+ priv->trmval0 = local_res.a0 & TSU_CODE_MAX;
+
+ arm_smccc_smc(RZ_SIP_SVC_GET_SYSTSU, OTP_TSU_REG_ADR_TEMPHI,
+ 0, 0, 0, 0, 0, 0, &local_res);
+ priv->trmval1 = local_res.a0 & TSU_CODE_MAX;
+
+ return 0;
+}
+
+static int rzg3e_thermal_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzg3e_thermal_priv *priv;
+ struct clk *clk;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ ret = devm_mutex_init(dev, &priv->lock);
+ if (ret)
+ return ret;
+ platform_set_drvdata(pdev, priv);
+
+ priv->info = device_get_match_data(dev);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = priv->info->get_trim(priv);
+ if (ret)
+ return ret;
+
+ if (!priv->trmval0 || !priv->trmval1 ||
+ priv->trmval0 == priv->trmval1 ||
+ priv->trmval0 == TSU_CODE_MAX || priv->trmval1 == TSU_CODE_MAX)
+ return dev_err_probe(priv->dev, -EINVAL,
+ "Invalid calibration: b=0x%03x, c=0x%03x\n",
+ priv->trmval0, priv->trmval1);
+
+ dev_dbg(priv->dev, "Calibration: b=0x%03x (%u), c=0x%03x (%u)\n",
+ priv->trmval0, priv->trmval0, priv->trmval1, priv->trmval1);
+
+ /* Get clock to verify frequency - clock is managed by power domain */
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to get clock\n");
+
+ if (clk_get_rate(clk) < TSU_MIN_CLOCK_RATE)
+ return dev_err_probe(dev, -EINVAL,
+ "Clock rate %lu Hz too low (min %u Hz)\n",
+ clk_get_rate(clk), TSU_MIN_CLOCK_RATE);
+
+ priv->rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(priv->rstc))
+ return dev_err_probe(dev, PTR_ERR(priv->rstc),
+ "Failed to get/deassert reset control\n");
+
+ /* Get comparison interrupt */
+ irq = platform_get_irq_byname(pdev, "adcmpi");
+ if (irq < 0)
+ return irq;
+
+ /* Enable runtime PM */
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ devm_pm_runtime_enable(dev);
+
+ /* Initial hardware setup */
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Runtime resume failed\n");
+
+ /* Register thermal zone - this will trigger DT parsing */
+ priv->zone = devm_thermal_of_zone_register(dev, 0, priv, &rzg3e_tz_ops);
+ if (IS_ERR(priv->zone)) {
+ ret = PTR_ERR(priv->zone);
+ dev_err(dev, "Failed to register thermal zone: %d\n", ret);
+ goto err_pm_put;
+ }
+
+ /* Request threaded IRQ for comparison interrupt */
+ ret = devm_request_threaded_irq(dev, irq, rzg3e_thermal_irq,
+ rzg3e_thermal_irq_thread,
+ IRQF_ONESHOT, "rzg3e_thermal", priv);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err_pm_put;
+ }
+
+ /* Add hwmon sysfs interface */
+ ret = devm_thermal_add_hwmon_sysfs(dev, priv->zone);
+ if (ret)
+ dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ dev_info(dev, "RZ/G3E thermal sensor registered\n");
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_put_sync(dev);
+ return ret;
+}
+
+static int rzg3e_thermal_runtime_suspend(struct device *dev)
+{
+ struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
+
+ rzg3e_thermal_power_off(priv);
+ return 0;
+}
+
+static int rzg3e_thermal_runtime_resume(struct device *dev)
+{
+ struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
+
+ return rzg3e_thermal_power_on(priv);
+}
+
+static int rzg3e_thermal_suspend(struct device *dev)
+{
+ struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
+
+ /* If device is active, power it off */
+ if (pm_runtime_active(dev))
+ rzg3e_thermal_power_off(priv);
+
+ /* Assert reset to ensure clean state after resume */
+ reset_control_assert(priv->rstc);
+
+ return 0;
+}
+
+static int rzg3e_thermal_resume(struct device *dev)
+{
+ struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ /* Deassert reset */
+ ret = reset_control_deassert(priv->rstc);
+ if (ret) {
+ dev_err(dev, "Failed to deassert reset: %d\n", ret);
+ return ret;
+ }
+
+ /* If device was active before suspend, power it back on */
+ if (pm_runtime_active(dev))
+ return rzg3e_thermal_power_on(priv);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rzg3e_thermal_pm_ops = {
+ RUNTIME_PM_OPS(rzg3e_thermal_runtime_suspend,
+ rzg3e_thermal_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(rzg3e_thermal_suspend, rzg3e_thermal_resume)
+};
+
+static const struct rzg3e_thermal_info rzg3e_thermal_info = {
+ .get_trim = rzg3e_thermal_get_syscon_trim,
+ .temp_d_mc = -41000,
+ .temp_e_mc = 126000,
+};
+
+static const struct rzg3e_thermal_info rzt2h_thermal_info = {
+ .get_trim = rzg3e_thermal_get_smc_trim,
+ .temp_d_mc = -40000,
+ .temp_e_mc = 125000,
+};
+
+static const struct of_device_id rzg3e_thermal_dt_ids[] = {
+ { .compatible = "renesas,r9a09g047-tsu", .data = &rzg3e_thermal_info },
+ { .compatible = "renesas,r9a09g077-tsu", .data = &rzt2h_thermal_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzg3e_thermal_dt_ids);
+
+static struct platform_driver rzg3e_thermal_driver = {
+ .driver = {
+ .name = "rzg3e_thermal",
+ .of_match_table = rzg3e_thermal_dt_ids,
+ .pm = pm_ptr(&rzg3e_thermal_pm_ops),
+ },
+ .probe = rzg3e_thermal_probe,
+};
+module_platform_driver(rzg3e_thermal_driver);
+
+MODULE_DESCRIPTION("Renesas RZ/G3E TSU Thermal Sensor Driver");
+MODULE_AUTHOR("John Madieu <john.madieu.xa@bp.renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c
new file mode 100644
index 000000000000..e25e36c99a88
--- /dev/null
+++ b/drivers/thermal/renesas/rzg3s_thermal.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G3S TSU Thermal Sensor Driver
+ *
+ * Copyright (C) 2024 Renesas Electronics Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/iio/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/thermal.h>
+#include <linux/units.h>
+
+#include "../thermal_hwmon.h"
+
+#define TSU_SM 0x0
+#define TSU_SM_EN BIT(0)
+#define TSU_SM_OE BIT(1)
+#define OTPTSUTRIM_REG(n) (0x18 + (n) * 0x4)
+#define OTPTSUTRIM_EN_MASK BIT(31)
+#define OTPTSUTRIM_MASK GENMASK(11, 0)
+
+#define TSU_READ_STEPS 8
+
+/* Default calibration values, if FUSE values are missing. */
+#define SW_CALIB0_VAL 1297
+#define SW_CALIB1_VAL 751
+
+#define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE)
+
+/**
+ * struct rzg3s_thermal_priv - RZ/G3S thermal private data structure
+ * @base: TSU base address
+ * @dev: device pointer
+ * @tz: thermal zone pointer
+ * @rstc: reset control
+ * @channel: IIO channel to read the TSU
+ * @mode: current device mode
+ * @calib0: calibration value
+ * @calib1: calibration value
+ */
+struct rzg3s_thermal_priv {
+ void __iomem *base;
+ struct device *dev;
+ struct thermal_zone_device *tz;
+ struct reset_control *rstc;
+ struct iio_channel *channel;
+ enum thermal_device_mode mode;
+ u16 calib0;
+ u16 calib1;
+};
+
+static int rzg3s_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
+ int ts_code_ave = 0;
+
+ if (priv->mode != THERMAL_DEVICE_ENABLED)
+ return -EAGAIN;
+
+ for (u8 i = 0; i < TSU_READ_STEPS; i++) {
+ int ret, val;
+
+ ret = iio_read_channel_raw(priv->channel, &val);
+ if (ret < 0)
+ return ret;
+
+ ts_code_ave += val;
+ /*
+ * According to the HW manual (Rev.1.10, section 40.4.4 Procedure for Measuring
+ * the Temperature) we need to wait here at leat 3us.
+ */
+ usleep_range(5, 10);
+ }
+
+ ts_code_ave = DIV_ROUND_CLOSEST(MCELSIUS(ts_code_ave), TSU_READ_STEPS);
+
+ /*
+ * According to the HW manual (Rev.1.10, section 40.4.4 Procedure for Measuring the
+ * Temperature) the computation formula is as follows:
+ *
+ * Tj = (ts_code_ave - priv->calib1) * 165 / (priv->calib0 - priv->calib1) - 40
+ *
+ * Convert everything to milli Celsius before applying the formula to avoid
+ * losing precision.
+ */
+
+ *temp = div_s64((s64)(ts_code_ave - MCELSIUS(priv->calib1)) * MCELSIUS(165),
+ MCELSIUS(priv->calib0 - priv->calib1)) - MCELSIUS(40);
+
+ /* Report it in milli degrees Celsius and round it up to 0.5 degrees Celsius. */
+ *temp = roundup(*temp, 500);
+
+ return 0;
+}
+
+static void rzg3s_thermal_set_mode(struct rzg3s_thermal_priv *priv,
+ enum thermal_device_mode mode)
+{
+ struct device *dev = priv->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return;
+
+ if (mode == THERMAL_DEVICE_DISABLED) {
+ writel(0, priv->base + TSU_SM);
+ } else {
+ writel(TSU_SM_EN, priv->base + TSU_SM);
+ /*
+ * According to the HW manual (Rev.1.10, section 40.4.1 Procedure for
+ * Starting the TSU) we need to wait here 30us or more.
+ */
+ usleep_range(30, 40);
+
+ writel(TSU_SM_OE | TSU_SM_EN, priv->base + TSU_SM);
+ /*
+ * According to the HW manual (Rev.1.10, section 40.4.1 Procedure for
+ * Starting the TSU) we need to wait here 50us or more.
+ */
+ usleep_range(50, 60);
+ }
+
+ pm_runtime_put_autosuspend(dev);
+}
+
+static int rzg3s_thermal_change_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode mode)
+{
+ struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
+
+ if (priv->mode == mode)
+ return 0;
+
+ rzg3s_thermal_set_mode(priv, mode);
+ priv->mode = mode;
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops rzg3s_tz_of_ops = {
+ .get_temp = rzg3s_thermal_get_temp,
+ .change_mode = rzg3s_thermal_change_mode,
+};
+
+static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv)
+{
+ struct device *dev = priv->dev;
+ u32 val;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ val = readl(priv->base + OTPTSUTRIM_REG(0));
+ if (val & OTPTSUTRIM_EN_MASK)
+ priv->calib0 = FIELD_GET(OTPTSUTRIM_MASK, val);
+ else
+ priv->calib0 = SW_CALIB0_VAL;
+
+ val = readl(priv->base + OTPTSUTRIM_REG(1));
+ if (val & OTPTSUTRIM_EN_MASK)
+ priv->calib1 = FIELD_GET(OTPTSUTRIM_MASK, val);
+ else
+ priv->calib1 = SW_CALIB1_VAL;
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int rzg3s_thermal_probe(struct platform_device *pdev)
+{
+ struct rzg3s_thermal_priv *priv;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->channel = devm_iio_channel_get(dev, "tsu");
+ if (IS_ERR(priv->channel))
+ return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n");
+
+ priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(priv->rstc))
+ return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n");
+
+ priv->dev = dev;
+ priv->mode = THERMAL_DEVICE_DISABLED;
+ platform_set_drvdata(pdev, priv);
+
+ pm_runtime_set_autosuspend_delay(dev, 300);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM!\n");
+
+ ret = rzg3s_thermal_read_calib(priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read calibration data!\n");
+
+ priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &rzg3s_tz_of_ops);
+ if (IS_ERR(priv->tz))
+ return dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n");
+
+ ret = devm_thermal_add_hwmon_sysfs(dev, priv->tz);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n");
+
+ return 0;
+}
+
+static int rzg3s_thermal_suspend(struct device *dev)
+{
+ struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev);
+
+ rzg3s_thermal_set_mode(priv, THERMAL_DEVICE_DISABLED);
+
+ return reset_control_assert(priv->rstc);
+}
+
+static int rzg3s_thermal_resume(struct device *dev)
+{
+ struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = reset_control_deassert(priv->rstc);
+ if (ret)
+ return ret;
+
+ if (priv->mode != THERMAL_DEVICE_DISABLED)
+ rzg3s_thermal_set_mode(priv, priv->mode);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rzg3s_thermal_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(rzg3s_thermal_suspend, rzg3s_thermal_resume)
+};
+
+static const struct of_device_id rzg3s_thermal_dt_ids[] = {
+ { .compatible = "renesas,r9a08g045-tsu" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzg3s_thermal_dt_ids);
+
+static struct platform_driver rzg3s_thermal_driver = {
+ .driver = {
+ .name = "rzg3s-thermal",
+ .of_match_table = rzg3s_thermal_dt_ids,
+ .pm = pm_ptr(&rzg3s_thermal_pm_ops),
+ },
+ .probe = rzg3s_thermal_probe,
+};
+module_platform_driver(rzg3s_thermal_driver);
+
+MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver");
+MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index f551df48eef9..c49ddf70f86e 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -9,6 +9,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -69,16 +70,19 @@ struct chip_tsadc_table {
* struct rockchip_tsadc_chip - hold the private data of tsadc chip
* @chn_offset: the channel offset of the first channel
* @chn_num: the channel number of tsadc chip
- * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @trim_slope: used to convert the trim code to a temperature in millicelsius
+ * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+ * @grf_required: true, if a GRF is required for proper functionality
* @initialize: SoC special initialize tsadc controller method
* @irq_ack: clear the interrupt
* @control: enable/disable method for the tsadc controller
- * @get_temp: get the temperature
+ * @get_temp: get the raw temperature, unadjusted by trim
* @set_alarm_temp: set the high temperature interrupt
* @set_tshut_temp: set the hardware-controlled shutdown temperature
* @set_tshut_mode: set the hardware-controlled shutdown mode
+ * @get_trim_code: convert a hardware temperature code to one adjusted for by trim
* @table: the chip-specific conversion table
*/
struct rockchip_tsadc_chip {
@@ -86,11 +90,17 @@ struct rockchip_tsadc_chip {
int chn_offset;
int chn_num;
+ /* Used to convert trim code to trim temp */
+ int trim_slope;
+
/* The hardware-controlled tshut property */
int tshut_temp;
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
+ /* GRF availability */
+ bool grf_required;
+
/* Chip-wide methods */
void (*initialize)(struct regmap *grf,
void __iomem *reg, enum tshut_polarity p);
@@ -105,6 +115,8 @@ struct rockchip_tsadc_chip {
int (*set_tshut_temp)(const struct chip_tsadc_table *table,
int chn, void __iomem *reg, int temp);
void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
+ int (*get_trim_code)(const struct chip_tsadc_table *table,
+ int code, int trim_base, int trim_base_frac);
/* Per-table methods */
struct chip_tsadc_table table;
@@ -114,12 +126,16 @@ struct rockchip_tsadc_chip {
* struct rockchip_thermal_sensor - hold the information of thermal sensor
* @thermal: pointer to the platform/configuration data
* @tzd: pointer to a thermal zone
+ * @of_node: pointer to the device_node representing this sensor, if any
* @id: identifier of the thermal sensor
+ * @trim_temp: per-sensor trim temperature value
*/
struct rockchip_thermal_sensor {
struct rockchip_thermal_data *thermal;
struct thermal_zone_device *tzd;
+ struct device_node *of_node;
int id;
+ int trim_temp;
};
/**
@@ -132,7 +148,11 @@ struct rockchip_thermal_sensor {
* @pclk: the advanced peripherals bus clock
* @grf: the general register file will be used to do static set by software
* @regs: the base address of tsadc controller
- * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @trim_base: major component of sensor trim value, in Celsius
+ * @trim_base_frac: minor component of sensor trim value, in Decicelsius
+ * @trim: fallback thermal trim value for each channel
+ * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
+ * @trim_temp: the fallback trim temperature for the whole sensor
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
*/
@@ -149,7 +169,12 @@ struct rockchip_thermal_data {
struct regmap *grf;
void __iomem *regs;
+ int trim_base;
+ int trim_base_frac;
+ int trim;
+
int tshut_temp;
+ int trim_temp;
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
};
@@ -249,6 +274,9 @@ struct rockchip_thermal_data {
#define GRF_CON_TSADC_CH_INV (0x10001 << 1)
+
+#define RK_MAX_TEMP (180000)
+
/**
* struct tsadc_table - code to temperature conversion table
* @code: the value of adc channel
@@ -386,6 +414,7 @@ static const struct tsadc_table rk3328_code_table[] = {
{296, -40000},
{304, -35000},
{313, -30000},
+ {322, -25000},
{331, -20000},
{340, -15000},
{349, -10000},
@@ -1044,7 +1073,7 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs,
writel_relaxed(val, regs + TSADCV2_INT_EN);
}
-static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs,
+static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs,
enum tshut_mode mode)
{
u32 val_gpio, val_cru;
@@ -1060,14 +1089,22 @@ static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs,
writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN);
}
+static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table,
+ int code, int trim_base, int trim_base_frac)
+{
+ int temp = trim_base * 1000 + trim_base_frac * 100;
+ u32 base_code = rk_tsadcv2_temp_to_code(table, temp);
+
+ return code - base_code;
+}
+
static const struct rockchip_tsadc_chip px30_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* 2 channels for tsadc */
-
+ .grf_required = true,
.tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv4_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1075,7 +1112,6 @@ static const struct rockchip_tsadc_chip px30_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3328_code_table,
.length = ARRAY_SIZE(rk3328_code_table),
@@ -1088,11 +1124,10 @@ static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
/* cpu */
.chn_offset = 0,
.chn_num = 1, /* one channel for tsadc */
-
+ .grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1100,7 +1135,6 @@ static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rv1108_table,
.length = ARRAY_SIZE(rv1108_table),
@@ -1113,11 +1147,10 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
/* cpu */
.chn_offset = 0,
.chn_num = 1, /* one channel for tsadc */
-
+ .grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1125,7 +1158,6 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3228_code_table,
.length = ARRAY_SIZE(rk3228_code_table),
@@ -1138,11 +1170,10 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
/* cpu, gpu */
.chn_offset = 1,
.chn_num = 2, /* two channels for tsadc */
-
+ .grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv2_irq_ack,
.control = rk_tsadcv2_control,
@@ -1150,7 +1181,6 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3288_code_table,
.length = ARRAY_SIZE(rk3288_code_table),
@@ -1163,10 +1193,9 @@ static const struct rockchip_tsadc_chip rk3328_tsadc_data = {
/* cpu */
.chn_offset = 0,
.chn_num = 1, /* one channels for tsadc */
-
+ .grf_required = false,
.tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1174,7 +1203,6 @@ static const struct rockchip_tsadc_chip rk3328_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3328_code_table,
.length = ARRAY_SIZE(rk3328_code_table),
@@ -1187,11 +1215,10 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
-
+ .grf_required = true,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv3_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1199,7 +1226,6 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3228_code_table,
.length = ARRAY_SIZE(rk3228_code_table),
@@ -1212,11 +1238,10 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
-
+ .grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv2_initialize,
.irq_ack = rk_tsadcv2_irq_ack,
.control = rk_tsadcv2_control,
@@ -1224,7 +1249,6 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3368_code_table,
.length = ARRAY_SIZE(rk3368_code_table),
@@ -1237,11 +1261,10 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
-
+ .grf_required = true,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv3_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1249,7 +1272,6 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3399_code_table,
.length = ARRAY_SIZE(rk3399_code_table),
@@ -1262,11 +1284,10 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
/* cpu, gpu */
.chn_offset = 0,
.chn_num = 2, /* two channels for tsadc */
-
+ .grf_required = true,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
-
.initialize = rk_tsadcv7_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
@@ -1274,7 +1295,6 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
.set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
-
.table = {
.id = rk3568_code_table,
.length = ARRAY_SIZE(rk3568_code_table),
@@ -1283,10 +1303,36 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
},
};
+static const struct rockchip_tsadc_chip rk3576_tsadc_data = {
+ /* top, big_core, little_core, ddr, npu, gpu */
+ .chn_offset = 0,
+ .chn_num = 6, /* six channels for tsadc */
+ .grf_required = false,
+ .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+ .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+ .tshut_temp = 95000,
+ .initialize = rk_tsadcv8_initialize,
+ .irq_ack = rk_tsadcv4_irq_ack,
+ .control = rk_tsadcv4_control,
+ .get_temp = rk_tsadcv4_get_temp,
+ .set_alarm_temp = rk_tsadcv3_alarm_temp,
+ .set_tshut_temp = rk_tsadcv3_tshut_temp,
+ .set_tshut_mode = rk_tsadcv4_tshut_mode,
+ .get_trim_code = rk_tsadcv2_get_trim_code,
+ .trim_slope = 923,
+ .table = {
+ .id = rk3588_code_table,
+ .length = ARRAY_SIZE(rk3588_code_table),
+ .data_mask = TSADCV4_DATA_MASK,
+ .mode = ADC_INCREMENT,
+ },
+};
+
static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
/* top, big_core0, big_core1, little_core, center, gpu, npu */
.chn_offset = 0,
.chn_num = 7, /* seven channels for tsadc */
+ .grf_required = false,
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
@@ -1296,7 +1342,7 @@ static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
.get_temp = rk_tsadcv4_get_temp,
.set_alarm_temp = rk_tsadcv3_alarm_temp,
.set_tshut_temp = rk_tsadcv3_tshut_temp,
- .set_tshut_mode = rk_tsadcv3_tshut_mode,
+ .set_tshut_mode = rk_tsadcv4_tshut_mode,
.table = {
.id = rk3588_code_table,
.length = ARRAY_SIZE(rk3588_code_table),
@@ -1342,6 +1388,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
.data = (void *)&rk3568_tsadc_data,
},
{
+ .compatible = "rockchip,rk3576-tsadc",
+ .data = (void *)&rk3576_tsadc_data,
+ },
+ {
.compatible = "rockchip,rk3588-tsadc",
.data = (void *)&rk3588_tsadc_data,
},
@@ -1386,7 +1436,7 @@ static int rockchip_thermal_set_trips(struct thermal_zone_device *tz, int low, i
__func__, sensor->id, low, high);
return tsadc->set_alarm_temp(&tsadc->table,
- sensor->id, thermal->regs, high);
+ sensor->id, thermal->regs, high + sensor->trim_temp);
}
static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp)
@@ -1398,6 +1448,8 @@ static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_te
retval = tsadc->get_temp(&tsadc->table,
sensor->id, thermal->regs, out_temp);
+ *out_temp -= sensor->trim_temp;
+
return retval;
}
@@ -1406,6 +1458,104 @@ static const struct thermal_zone_device_ops rockchip_of_thermal_ops = {
.set_trips = rockchip_thermal_set_trips,
};
+/**
+ * rockchip_get_efuse_value - read an OTP cell from a device node
+ * @np: pointer to the device node with the nvmem-cells property
+ * @cell_name: name of cell that should be read
+ * @value: pointer to where the read value will be placed
+ *
+ * Return: Negative errno on failure, during which *value will not be touched,
+ * or 0 on success.
+ */
+static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name,
+ int *value)
+{
+ struct nvmem_cell *cell;
+ int ret = 0;
+ size_t len;
+ u8 *buf;
+ int i;
+
+ cell = of_nvmem_cell_get(np, cell_name);
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ buf = nvmem_cell_read(cell, &len);
+
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ if (len > sizeof(*value)) {
+ ret = -ERANGE;
+ goto exit;
+ }
+
+ /* Copy with implicit endian conversion */
+ *value = 0;
+ for (i = 0; i < len; i++)
+ *value |= (int) buf[i] << (8 * i);
+
+exit:
+ kfree(buf);
+ return ret;
+}
+
+static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np,
+ struct rockchip_thermal_data *thermal)
+{
+ const struct rockchip_tsadc_chip *tsadc = thermal->chip;
+ int trim_base = 0, trim_base_frac = 0, trim = 0;
+ int trim_code;
+ int ret;
+
+ thermal->trim_base = 0;
+ thermal->trim_base_frac = 0;
+ thermal->trim = 0;
+
+ if (!tsadc->get_trim_code)
+ return 0;
+
+ ret = rockchip_get_efuse_value(np, "trim_base", &trim_base);
+ if (ret < 0) {
+ if (ret == -ENOENT) {
+ trim_base = 30;
+ dev_dbg(dev, "trim_base is absent, defaulting to 30\n");
+ } else {
+ dev_err(dev, "failed reading nvmem value of trim_base: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+ ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac);
+ if (ret < 0) {
+ if (ret == -ENOENT) {
+ dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n");
+ } else {
+ dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+ thermal->trim_base = trim_base;
+ thermal->trim_base_frac = trim_base_frac;
+
+ /*
+ * If the tsadc node contains the trim property, then it is used in the
+ * absence of per-channel trim values
+ */
+ if (!rockchip_get_efuse_value(np, "trim", &trim))
+ thermal->trim = trim;
+ if (trim) {
+ trim_code = tsadc->get_trim_code(&tsadc->table, trim,
+ trim_base, trim_base_frac);
+ thermal->trim_temp = thermal->chip->trim_slope * trim_code;
+ }
+
+ return 0;
+}
+
static int rockchip_configure_from_dt(struct device *dev,
struct device_node *np,
struct rockchip_thermal_data *thermal)
@@ -1459,12 +1609,12 @@ static int rockchip_configure_from_dt(struct device *dev,
return -EINVAL;
}
- /* The tsadc wont to handle the error in here since some SoCs didn't
- * need this property.
- */
thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
- if (IS_ERR(thermal->grf))
- dev_warn(dev, "Missing rockchip,grf property\n");
+ if (IS_ERR(thermal->grf) && thermal->chip->grf_required)
+ return dev_err_probe(dev, PTR_ERR(thermal->grf),
+ "Missing rockchip,grf property\n");
+
+ rockchip_get_trim_configuration(dev, np, thermal);
return 0;
}
@@ -1476,23 +1626,50 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
int id)
{
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
+ struct device *dev = &pdev->dev;
+ int trim = thermal->trim;
+ int trim_code, tshut_temp;
+ int trim_temp = 0;
int error;
+ if (thermal->trim_temp)
+ trim_temp = thermal->trim_temp;
+
+ if (tsadc->get_trim_code && sensor->of_node) {
+ error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim);
+ if (error < 0 && error != -ENOENT) {
+ dev_err(dev, "failed reading trim of sensor %d: %pe\n",
+ id, ERR_PTR(error));
+ return error;
+ }
+ if (trim) {
+ trim_code = tsadc->get_trim_code(&tsadc->table, trim,
+ thermal->trim_base,
+ thermal->trim_base_frac);
+ trim_temp = thermal->chip->trim_slope * trim_code;
+ }
+ }
+
+ sensor->trim_temp = trim_temp;
+
+ dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp);
+
+ tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP);
+
tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode);
- error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs,
- thermal->tshut_temp);
+ error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp);
if (error)
- dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n",
- __func__, thermal->tshut_temp, error);
+ dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
+ __func__, tshut_temp, error);
sensor->thermal = thermal;
sensor->id = id;
- sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor,
+ sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor,
&rockchip_of_thermal_ops);
if (IS_ERR(sensor->tzd)) {
error = PTR_ERR(sensor->tzd);
- dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
+ dev_err(dev, "failed to register sensor %d: %d\n",
id, error);
return error;
}
@@ -1515,9 +1692,11 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct rockchip_thermal_data *thermal;
+ struct device_node *child;
int irq;
int i;
int error;
+ u32 chn;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -1568,6 +1747,18 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
thermal->chip->initialize(thermal->grf, thermal->regs,
thermal->tshut_polarity);
+ for_each_available_child_of_node(np, child) {
+ if (!of_property_read_u32(child, "reg", &chn)) {
+ if (chn < thermal->chip->chn_num)
+ thermal->sensors[chn].of_node = child;
+ else
+ dev_warn(&pdev->dev,
+ "sensor address (%d) too large, ignoring its trim\n",
+ chn);
+ }
+
+ }
+
for (i = 0; i < thermal->chip->chn_num; i++) {
error = rockchip_thermal_register_sensor(pdev, thermal,
&thermal->sensors[i],
@@ -1637,8 +1828,11 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
static int __maybe_unused rockchip_thermal_resume(struct device *dev)
{
struct rockchip_thermal_data *thermal = dev_get_drvdata(dev);
- int i;
+ const struct rockchip_tsadc_chip *tsadc = thermal->chip;
+ struct rockchip_thermal_sensor *sensor;
+ int tshut_temp;
int error;
+ int i;
error = clk_enable(thermal->clk);
if (error)
@@ -1652,21 +1846,23 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
rockchip_thermal_reset_controller(thermal->reset);
- thermal->chip->initialize(thermal->grf, thermal->regs,
- thermal->tshut_polarity);
+ tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity);
for (i = 0; i < thermal->chip->chn_num; i++) {
- int id = thermal->sensors[i].id;
+ sensor = &thermal->sensors[i];
+
+ tshut_temp = min(thermal->tshut_temp + sensor->trim_temp,
+ RK_MAX_TEMP);
- thermal->chip->set_tshut_mode(id, thermal->regs,
+ tsadc->set_tshut_mode(sensor->id, thermal->regs,
thermal->tshut_mode);
- error = thermal->chip->set_tshut_temp(&thermal->chip->table,
- id, thermal->regs,
- thermal->tshut_temp);
+ error = tsadc->set_tshut_temp(&thermal->chip->table,
+ sensor->id, thermal->regs,
+ tshut_temp);
if (error)
dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
- __func__, thermal->tshut_temp, error);
+ __func__, tshut_temp, error);
}
thermal->chip->control(thermal->regs, true);
diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c
index bb96be947521..5e3e9c1f32f8 100644
--- a/drivers/thermal/spear_thermal.c
+++ b/drivers/thermal/spear_thermal.c
@@ -41,7 +41,7 @@ static inline int thermal_get_temp(struct thermal_zone_device *thermal,
return 0;
}
-static struct thermal_zone_device_ops ops = {
+static const struct thermal_zone_device_ops ops = {
.get_temp = thermal_get_temp,
};
@@ -93,7 +93,7 @@ static int spear_thermal_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
int ret = 0, val;
- if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
+ if (!np || of_property_read_u32(np, "st,thermal-flags", &val)) {
dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");
return -EINVAL;
}
diff --git a/drivers/thermal/sprd_thermal.c b/drivers/thermal/sprd_thermal.c
index e546067c9621..d683fcb0f8ab 100644
--- a/drivers/thermal/sprd_thermal.c
+++ b/drivers/thermal/sprd_thermal.c
@@ -178,7 +178,7 @@ static int sprd_thm_sensor_calibration(struct device_node *np,
static int sprd_thm_rawdata_to_temp(struct sprd_thermal_sensor *sen,
u32 rawdata)
{
- clamp(rawdata, (u32)SPRD_THM_RAW_DATA_LOW, (u32)SPRD_THM_RAW_DATA_HIGH);
+ rawdata = clamp(rawdata, SPRD_THM_RAW_DATA_LOW, SPRD_THM_RAW_DATA_HIGH);
/*
* According to the thermal datasheet, the formula of converting
@@ -192,7 +192,7 @@ static int sprd_thm_temp_to_rawdata(int temp, struct sprd_thermal_sensor *sen)
{
u32 val;
- clamp(temp, (int)SPRD_THM_TEMP_LOW, (int)SPRD_THM_TEMP_HIGH);
+ temp = clamp(temp, SPRD_THM_TEMP_LOW, SPRD_THM_TEMP_HIGH);
/*
* According to the thermal datasheet, the formula of converting
@@ -201,7 +201,7 @@ static int sprd_thm_temp_to_rawdata(int temp, struct sprd_thermal_sensor *sen)
*/
val = (temp + sen->cal_offset) / sen->cal_slope;
- return clamp(val, val, (u32)(SPRD_THM_RAW_DATA_HIGH - 1));
+ return min(val, SPRD_THM_RAW_DATA_HIGH - 1);
}
static int sprd_thm_read_temp(struct thermal_zone_device *tz, int *temp)
diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c
index a14a37d54698..1470ca519def 100644
--- a/drivers/thermal/st/st_thermal.c
+++ b/drivers/thermal/st/st_thermal.c
@@ -132,7 +132,7 @@ static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
return 0;
}
-static struct thermal_zone_device_ops st_tz_ops = {
+static const struct thermal_zone_device_ops st_tz_ops = {
.get_temp = st_thermal_get_temp,
};
diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c
index 6e90eb9f414d..5d8170bfb382 100644
--- a/drivers/thermal/st/stm_thermal.c
+++ b/drivers/thermal/st/stm_thermal.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
+#include <linux/units.h>
#include "../thermal_hwmon.h"
@@ -76,7 +77,6 @@
/* Constants */
#define ADJUST 100
-#define ONE_MHZ 1000000
#define POLL_TIMEOUT 5000
#define STARTUP_TIME 40
#define TS1_T0_VAL0 30000 /* 30 celsius */
@@ -205,7 +205,7 @@ static int stm_thermal_calibration(struct stm_thermal_sensor *sensor)
return -EINVAL;
prescaler = 0;
- clk_freq /= ONE_MHZ;
+ clk_freq /= HZ_PER_MHZ;
if (clk_freq) {
while (prescaler <= clk_freq)
prescaler++;
diff --git a/drivers/thermal/tegra/Makefile b/drivers/thermal/tegra/Makefile
index eb27d194c583..9b3e91f7fb97 100644
--- a/drivers/thermal/tegra/Makefile
+++ b/drivers/thermal/tegra/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_TEGRA_BPMP_THERMAL) += tegra-bpmp-thermal.o
obj-$(CONFIG_TEGRA30_TSENSOR) += tegra30-tsensor.o
tegra-soctherm-y := soctherm.o soctherm-fuse.o
+tegra-soctherm-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-soctherm.o
tegra-soctherm-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-soctherm.o
diff --git a/drivers/thermal/tegra/soctherm-fuse.c b/drivers/thermal/tegra/soctherm-fuse.c
index 190f95280e0b..8d37cd8c9122 100644
--- a/drivers/thermal/tegra/soctherm-fuse.c
+++ b/drivers/thermal/tegra/soctherm-fuse.c
@@ -9,15 +9,12 @@
#include "soctherm.h"
-#define NOMINAL_CALIB_FT 105
#define NOMINAL_CALIB_CP 25
#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
-#define FUSE_TSENSOR_COMMON 0x180
-
/*
* Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
* 3 2 1 0
@@ -26,7 +23,7 @@
* | BASE_FT | BASE_CP | SHFT_FT | SHIFT_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
- * Tegra12x, etc:
+ * Tegra124:
* In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
* and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
* were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
@@ -44,6 +41,13 @@
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |---------------------------------------------------| SHIFT_CP |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Tegra114: Layout of bits in FUSE_TSENSOR_COMMON aka FUSE_VSENSOR_CALIB:
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SHFT_FT | BASE_FT | SHIFT_CP | BASE_CP |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#define CALIB_COEFFICIENT 1000000LL
@@ -77,7 +81,7 @@ int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
s32 shifted_cp, shifted_ft;
int err;
- err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
+ err = tegra_fuse_readl(tfuse->fuse_common_reg, &val);
if (err)
return err;
@@ -96,10 +100,12 @@ int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
return err;
}
+ shifted_cp = (val & tfuse->fuse_shift_cp_mask) >>
+ tfuse->fuse_shift_cp_shift;
shifted_cp = sign_extend32(val, 5);
shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
- shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
+ shared->actual_temp_ft = 2 * tfuse->nominal_calib_ft + shifted_ft;
return 0;
}
diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c
index 2c5ddf0db40c..5d26b52beaba 100644
--- a/drivers/thermal/tegra/soctherm.c
+++ b/drivers/thermal/tegra/soctherm.c
@@ -31,6 +31,7 @@
#include <linux/reset.h>
#include <linux/thermal.h>
+#include <dt-bindings/thermal/tegra114-soctherm.h>
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include "../thermal_core.h"
@@ -357,6 +358,12 @@ struct soctherm_oc_irq_chip_data {
static struct soctherm_oc_irq_chip_data soc_irq_cdata;
+/* Ensure that TEGRA114_* and TEGRA124_* counterparts are equal */
+static_assert(TEGRA114_SOCTHERM_SENSOR_CPU == TEGRA124_SOCTHERM_SENSOR_CPU);
+static_assert(TEGRA114_SOCTHERM_SENSOR_MEM == TEGRA124_SOCTHERM_SENSOR_MEM);
+static_assert(TEGRA114_SOCTHERM_SENSOR_GPU == TEGRA124_SOCTHERM_SENSOR_GPU);
+static_assert(TEGRA114_SOCTHERM_SENSOR_PLLX == TEGRA124_SOCTHERM_SENSOR_PLLX);
+
/**
* ccroc_writel() - writes a value to a CCROC register
* @ts: pointer to a struct tegra_soctherm
@@ -1206,7 +1213,7 @@ static const struct irq_domain_ops soctherm_oc_domain_ops = {
/**
* soctherm_oc_int_init() - Initial enabling of the over
* current interrupts
- * @np: The devicetree node for soctherm
+ * @fwnode: The devicetree node for soctherm
* @num_irqs: The number of new interrupt requests
*
* Sets the over current interrupt request chip data
@@ -1215,7 +1222,7 @@ static const struct irq_domain_ops soctherm_oc_domain_ops = {
* -ENOMEM (out of memory), or irq_base if the function failed to
* allocate the irqs
*/
-static int soctherm_oc_int_init(struct device_node *np, int num_irqs)
+static int soctherm_oc_int_init(struct fwnode_handle *fwnode, int num_irqs)
{
if (!num_irqs) {
pr_info("%s(): OC interrupts are not enabled\n", __func__);
@@ -1234,10 +1241,8 @@ static int soctherm_oc_int_init(struct device_node *np, int num_irqs)
soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type;
soc_irq_cdata.irq_chip.irq_set_wake = NULL;
- soc_irq_cdata.domain = irq_domain_add_linear(np, num_irqs,
- &soctherm_oc_domain_ops,
- &soc_irq_cdata);
-
+ soc_irq_cdata.domain = irq_domain_create_linear(fwnode, num_irqs, &soctherm_oc_domain_ops,
+ &soc_irq_cdata);
if (!soc_irq_cdata.domain) {
pr_err("%s: Failed to create IRQ domain\n", __func__);
return -ENOMEM;
@@ -1968,10 +1973,9 @@ static void tegra_soctherm_throttle(struct device *dev)
static int soctherm_interrupts_init(struct platform_device *pdev,
struct tegra_soctherm *tegra)
{
- struct device_node *np = pdev->dev.of_node;
int ret;
- ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX);
+ ret = soctherm_oc_int_init(dev_fwnode(&pdev->dev), TEGRA_SOC_OC_IRQ_MAX);
if (ret < 0) {
dev_err(&pdev->dev, "soctherm_oc_int_init failed\n");
return ret;
@@ -2048,6 +2052,12 @@ static void soctherm_init(struct platform_device *pdev)
}
static const struct of_device_id tegra_soctherm_of_match[] = {
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+ {
+ .compatible = "nvidia,tegra114-soctherm",
+ .data = &tegra114_soctherm,
+ },
+#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
{
.compatible = "nvidia,tegra124-soctherm",
diff --git a/drivers/thermal/tegra/soctherm.h b/drivers/thermal/tegra/soctherm.h
index 70501e73d586..aa4af9268b05 100644
--- a/drivers/thermal/tegra/soctherm.h
+++ b/drivers/thermal/tegra/soctherm.h
@@ -56,6 +56,9 @@
#define SENSOR_TEMP2_MEM_TEMP_MASK (0xffff << 16)
#define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff
+#define FUSE_VSENSOR_CALIB 0x08c
+#define FUSE_TSENSOR_COMMON 0x180
+
/**
* struct tegra_tsensor_group - SOC_THERM sensor group data
* @name: short name of the temperature sensor group
@@ -109,9 +112,11 @@ struct tsensor_group_thermtrips {
struct tegra_soctherm_fuse {
u32 fuse_base_cp_mask, fuse_base_cp_shift;
+ u32 fuse_shift_cp_mask, fuse_shift_cp_shift;
u32 fuse_base_ft_mask, fuse_base_ft_shift;
u32 fuse_shift_ft_mask, fuse_shift_ft_shift;
- u32 fuse_spare_realignment;
+ u32 fuse_common_reg, fuse_spare_realignment;
+ u32 nominal_calib_ft;
};
struct tsensor_shared_calib {
@@ -137,6 +142,10 @@ int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
const struct tsensor_shared_calib *shared,
u32 *calib);
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+extern const struct tegra_soctherm_soc tegra114_soctherm;
+#endif
+
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_soctherm_soc tegra124_soctherm;
#endif
diff --git a/drivers/thermal/tegra/tegra114-soctherm.c b/drivers/thermal/tegra/tegra114-soctherm.c
new file mode 100644
index 000000000000..688104f28052
--- /dev/null
+++ b/drivers/thermal/tegra/tegra114-soctherm.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2024, Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/thermal/tegra114-soctherm.h>
+
+#include "soctherm.h"
+
+#define TEGRA114_THERMTRIP_ANY_EN_MASK (0x1 << 28)
+#define TEGRA114_THERMTRIP_MEM_EN_MASK (0x1 << 27)
+#define TEGRA114_THERMTRIP_GPU_EN_MASK (0x1 << 26)
+#define TEGRA114_THERMTRIP_CPU_EN_MASK (0x1 << 25)
+#define TEGRA114_THERMTRIP_TSENSE_EN_MASK (0x1 << 24)
+#define TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK (0xff << 16)
+#define TEGRA114_THERMTRIP_CPU_THRESH_MASK (0xff << 8)
+#define TEGRA114_THERMTRIP_TSENSE_THRESH_MASK 0xff
+
+#define TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17)
+#define TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9)
+
+#define TEGRA114_THRESH_GRAIN 1000
+#define TEGRA114_BPTT 8
+
+static const struct tegra_tsensor_configuration tegra114_tsensor_config = {
+ .tall = 16300,
+ .tiddq_en = 1,
+ .ten_count = 1,
+ .tsample = 163,
+ .tsample_ate = 655,
+};
+
+static const struct tegra_tsensor_group tegra114_tsensor_group_cpu = {
+ .id = TEGRA114_SOCTHERM_SENSOR_CPU,
+ .name = "cpu",
+ .sensor_temp_offset = SENSOR_TEMP1,
+ .sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
+ .pdiv = 10,
+ .pdiv_ate = 10,
+ .pdiv_mask = SENSOR_PDIV_CPU_MASK,
+ .pllx_hotspot_diff = 6,
+ .pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
+ .thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
+ .thermtrip_enable_mask = TEGRA114_THERMTRIP_CPU_EN_MASK,
+ .thermtrip_threshold_mask = TEGRA114_THERMTRIP_CPU_THRESH_MASK,
+ .thermctl_isr_mask = THERM_IRQ_CPU_MASK,
+ .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+ .thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
+ .thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra114_tsensor_group_gpu = {
+ .id = TEGRA114_SOCTHERM_SENSOR_GPU,
+ .name = "gpu",
+ .sensor_temp_offset = SENSOR_TEMP1,
+ .sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
+ .pdiv = 10,
+ .pdiv_ate = 10,
+ .pdiv_mask = SENSOR_PDIV_GPU_MASK,
+ .pllx_hotspot_diff = 6,
+ .pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
+ .thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
+ .thermtrip_enable_mask = TEGRA114_THERMTRIP_GPU_EN_MASK,
+ .thermtrip_threshold_mask = TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK,
+ .thermctl_isr_mask = THERM_IRQ_GPU_MASK,
+ .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+ .thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
+ .thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra114_tsensor_group_pll = {
+ .id = TEGRA114_SOCTHERM_SENSOR_PLLX,
+ .name = "pll",
+ .sensor_temp_offset = SENSOR_TEMP2,
+ .sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
+ .pdiv = 10,
+ .pdiv_ate = 10,
+ .pdiv_mask = SENSOR_PDIV_PLLX_MASK,
+ .thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
+ .thermtrip_enable_mask = TEGRA114_THERMTRIP_TSENSE_EN_MASK,
+ .thermtrip_threshold_mask = TEGRA114_THERMTRIP_TSENSE_THRESH_MASK,
+ .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK,
+ .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+ .thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
+ .thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra114_tsensor_group_mem = {
+ .id = TEGRA114_SOCTHERM_SENSOR_MEM,
+ .name = "mem",
+ .sensor_temp_offset = SENSOR_TEMP2,
+ .sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
+ .pdiv = 10,
+ .pdiv_ate = 10,
+ .pdiv_mask = SENSOR_PDIV_MEM_MASK,
+ .pllx_hotspot_diff = 0,
+ .pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
+ .thermtrip_any_en_mask = TEGRA114_THERMTRIP_ANY_EN_MASK,
+ .thermtrip_enable_mask = TEGRA114_THERMTRIP_MEM_EN_MASK,
+ .thermtrip_threshold_mask = TEGRA114_THERMTRIP_GPUMEM_THRESH_MASK,
+ .thermctl_isr_mask = THERM_IRQ_MEM_MASK,
+ .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+ .thermctl_lvl0_up_thresh_mask = TEGRA114_THERMCTL_LVL0_UP_THRESH_MASK,
+ .thermctl_lvl0_dn_thresh_mask = TEGRA114_THERMCTL_LVL0_DN_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group *tegra114_tsensor_groups[] = {
+ &tegra114_tsensor_group_cpu,
+ &tegra114_tsensor_group_gpu,
+ &tegra114_tsensor_group_pll,
+ &tegra114_tsensor_group_mem,
+};
+
+static const struct tegra_tsensor tegra114_tsensors[] = {
+ {
+ .name = "cpu0",
+ .base = 0xc0,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x098,
+ .fuse_corr_alpha = 1196400,
+ .fuse_corr_beta = -13600000,
+ .group = &tegra114_tsensor_group_cpu,
+ }, {
+ .name = "cpu1",
+ .base = 0xe0,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x084,
+ .fuse_corr_alpha = 1196400,
+ .fuse_corr_beta = -13600000,
+ .group = &tegra114_tsensor_group_cpu,
+ }, {
+ .name = "cpu2",
+ .base = 0x100,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x088,
+ .fuse_corr_alpha = 1196400,
+ .fuse_corr_beta = -13600000,
+ .group = &tegra114_tsensor_group_cpu,
+ }, {
+ .name = "cpu3",
+ .base = 0x120,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x12c,
+ .fuse_corr_alpha = 1196400,
+ .fuse_corr_beta = -13600000,
+ .group = &tegra114_tsensor_group_cpu,
+ }, {
+ .name = "mem0",
+ .base = 0x140,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x158,
+ .fuse_corr_alpha = 1000000,
+ .fuse_corr_beta = 0,
+ .group = &tegra114_tsensor_group_mem,
+ }, {
+ .name = "mem1",
+ .base = 0x160,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x15c,
+ .fuse_corr_alpha = 1000000,
+ .fuse_corr_beta = 0,
+ .group = &tegra114_tsensor_group_mem,
+ }, {
+ .name = "gpu",
+ .base = 0x180,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x154,
+ .fuse_corr_alpha = 1124500,
+ .fuse_corr_beta = -9793100,
+ .group = &tegra114_tsensor_group_gpu,
+ }, {
+ .name = "pllx",
+ .base = 0x1a0,
+ .config = &tegra114_tsensor_config,
+ .calib_fuse_offset = 0x160,
+ .fuse_corr_alpha = 1224200,
+ .fuse_corr_beta = -14665000,
+ .group = &tegra114_tsensor_group_pll,
+ },
+};
+
+static const struct tegra_soctherm_fuse tegra114_soctherm_fuse = {
+ .fuse_base_cp_mask = 0x3ff,
+ .fuse_base_cp_shift = 0,
+ .fuse_shift_cp_mask = 0x3f << 10,
+ .fuse_shift_cp_shift = 10,
+ .fuse_base_ft_mask = 0x7ff << 16,
+ .fuse_base_ft_shift = 16,
+ .fuse_shift_ft_mask = 0x1f << 27,
+ .fuse_shift_ft_shift = 27,
+ .fuse_common_reg = FUSE_VSENSOR_CALIB,
+ .fuse_spare_realignment = 0,
+ .nominal_calib_ft = 90,
+};
+
+const struct tegra_soctherm_soc tegra114_soctherm = {
+ .tsensors = tegra114_tsensors,
+ .num_tsensors = ARRAY_SIZE(tegra114_tsensors),
+ .ttgs = tegra114_tsensor_groups,
+ .num_ttgs = ARRAY_SIZE(tegra114_tsensor_groups),
+ .tfuse = &tegra114_soctherm_fuse,
+ .thresh_grain = TEGRA114_THRESH_GRAIN,
+ .bptt = TEGRA114_BPTT,
+ .use_ccroc = false,
+};
diff --git a/drivers/thermal/tegra/tegra124-soctherm.c b/drivers/thermal/tegra/tegra124-soctherm.c
index 20ad27f4d1a1..d86acff1b234 100644
--- a/drivers/thermal/tegra/tegra124-soctherm.c
+++ b/drivers/thermal/tegra/tegra124-soctherm.c
@@ -200,11 +200,15 @@ static const struct tegra_tsensor tegra124_tsensors[] = {
static const struct tegra_soctherm_fuse tegra124_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
+ .fuse_shift_cp_mask = 0x3f,
+ .fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
+ .fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0x1fc,
+ .nominal_calib_ft = 105,
};
const struct tegra_soctherm_soc tegra124_soctherm = {
diff --git a/drivers/thermal/tegra/tegra132-soctherm.c b/drivers/thermal/tegra/tegra132-soctherm.c
index b76308fdad9e..64c0363b9717 100644
--- a/drivers/thermal/tegra/tegra132-soctherm.c
+++ b/drivers/thermal/tegra/tegra132-soctherm.c
@@ -200,11 +200,15 @@ static struct tegra_tsensor tegra132_tsensors[] = {
static const struct tegra_soctherm_fuse tegra132_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff,
.fuse_base_cp_shift = 0,
+ .fuse_shift_cp_mask = 0x3f,
+ .fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 10,
.fuse_base_ft_shift = 10,
.fuse_shift_ft_mask = 0x1f << 21,
.fuse_shift_ft_shift = 21,
+ .fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0x1fc,
+ .nominal_calib_ft = 105,
};
const struct tegra_soctherm_soc tegra132_soctherm = {
diff --git a/drivers/thermal/tegra/tegra210-soctherm.c b/drivers/thermal/tegra/tegra210-soctherm.c
index d0ff793f18c5..f6e1493f0202 100644
--- a/drivers/thermal/tegra/tegra210-soctherm.c
+++ b/drivers/thermal/tegra/tegra210-soctherm.c
@@ -201,11 +201,15 @@ static const struct tegra_tsensor tegra210_tsensors[] = {
static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = {
.fuse_base_cp_mask = 0x3ff << 11,
.fuse_base_cp_shift = 11,
+ .fuse_shift_cp_mask = 0x3f,
+ .fuse_shift_cp_shift = 0,
.fuse_base_ft_mask = 0x7ff << 21,
.fuse_base_ft_shift = 21,
.fuse_shift_ft_mask = 0x1f << 6,
.fuse_shift_ft_shift = 6,
+ .fuse_common_reg = FUSE_TSENSOR_COMMON,
.fuse_spare_realignment = 0,
+ .nominal_calib_ft = 105,
};
static struct tsensor_group_thermtrips tegra210_tsensor_thermtrips[] = {
diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c
index ba11d70e8021..1159ecea57e7 100644
--- a/drivers/thermal/testing/command.c
+++ b/drivers/thermal/testing/command.c
@@ -139,31 +139,21 @@ static int tt_command_exec(int index, const char *arg)
return ret;
}
-static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf,
- size_t count)
+static ssize_t tt_command_process(char *s)
{
- char *buf __free(kfree);
char *arg;
int i;
- buf = kmalloc(count + 1, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ strim(s);
- if (copy_from_user(buf, user_buf, count))
- return -EFAULT;
-
- buf[count] = '\0';
- strim(buf);
-
- arg = strstr(buf, ":");
+ arg = strchr(s, ':');
if (arg) {
*arg = '\0';
arg++;
}
for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) {
- if (!strcmp(buf, tt_command_strings[i]))
+ if (!strcmp(s, tt_command_strings[i]))
return tt_command_exec(i, arg);
}
@@ -173,20 +163,20 @@ static ssize_t tt_command_process(struct dentry *dentry, const char __user *user
static ssize_t tt_command_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct dentry *dentry = file->f_path.dentry;
+ char buf[TT_COMMAND_SIZE];
ssize_t ret;
if (*ppos)
return -EINVAL;
- if (count + 1 > TT_COMMAND_SIZE)
+ if (count > TT_COMMAND_SIZE - 1)
return -E2BIG;
- ret = debugfs_file_get(dentry);
- if (unlikely(ret))
- return ret;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ buf[count] = '\0';
- ret = tt_command_process(dentry, user_buf, count);
+ ret = tt_command_process(buf);
if (ret)
return ret;
diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c
index 1f4e450100e2..3c339242f52d 100644
--- a/drivers/thermal/testing/zone.c
+++ b/drivers/thermal/testing/zone.c
@@ -184,15 +184,13 @@ static void tt_add_tz_work_fn(struct work_struct *work)
int tt_add_tz(void)
{
- struct tt_thermal_zone *tt_zone __free(kfree);
- struct tt_work *tt_work __free(kfree) = NULL;
int ret;
- tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL);
+ struct tt_thermal_zone *tt_zone __free(kfree) = kzalloc_obj(*tt_zone);
if (!tt_zone)
return -ENOMEM;
- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
+ struct tt_work *tt_work __free(kfree) = kzalloc_obj(*tt_work);
if (!tt_work)
return -ENOMEM;
@@ -237,7 +235,6 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone)
int tt_del_tz(const char *arg)
{
- struct tt_work *tt_work __free(kfree) = NULL;
struct tt_thermal_zone *tt_zone, *aux;
int ret;
int id;
@@ -246,7 +243,7 @@ int tt_del_tz(const char *arg)
if (ret != 1)
return -EINVAL;
- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
+ struct tt_work *tt_work __free(kfree) = kzalloc_obj(*tt_work);
if (!tt_work)
return -ENOMEM;
@@ -330,20 +327,17 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work)
int tt_zone_add_trip(const char *arg)
{
- struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL;
- struct tt_trip *tt_trip __free(kfree) = NULL;
- struct tt_work *tt_work __free(kfree);
int id;
- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
+ struct tt_work *tt_work __free(kfree) = kzalloc_obj(*tt_work);
if (!tt_work)
return -ENOMEM;
- tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL);
+ struct tt_trip *tt_trip __free(kfree) = kzalloc_obj(*tt_trip);
if (!tt_trip)
return -ENOMEM;
- tt_zone = tt_get_tt_zone(arg);
+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg);
if (IS_ERR(tt_zone))
return PTR_ERR(tt_zone);
@@ -381,13 +375,12 @@ static int tt_zone_get_temp(struct thermal_zone_device *tz, int *temp)
return 0;
}
-static struct thermal_zone_device_ops tt_zone_ops = {
+static const struct thermal_zone_device_ops tt_zone_ops = {
.get_temp = tt_zone_get_temp,
};
static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
{
- struct thermal_trip *trips __free(kfree) = NULL;
struct thermal_zone_device *tz;
struct tt_trip *tt_trip;
int i;
@@ -397,7 +390,8 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
if (tt_zone->tz)
return -EINVAL;
- trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL);
+ struct thermal_trip *trips __free(kfree) = kzalloc_objs(*trips,
+ tt_zone->num_trips);
if (!trips)
return -ENOMEM;
@@ -421,9 +415,7 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
int tt_zone_reg(const char *arg)
{
- struct tt_thermal_zone *tt_zone __free(put_tt_zone);
-
- tt_zone = tt_get_tt_zone(arg);
+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg);
if (IS_ERR(tt_zone))
return PTR_ERR(tt_zone);
@@ -432,9 +424,7 @@ int tt_zone_reg(const char *arg)
int tt_zone_unreg(const char *arg)
{
- struct tt_thermal_zone *tt_zone __free(put_tt_zone);
-
- tt_zone = tt_get_tt_zone(arg);
+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg);
if (IS_ERR(tt_zone))
return PTR_ERR(tt_zone);
diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c
index ee3d0aa31406..7c844589b153 100644
--- a/drivers/thermal/thermal-generic-adc.c
+++ b/drivers/thermal/thermal-generic-adc.c
@@ -7,6 +7,7 @@
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*/
#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -73,6 +74,58 @@ static const struct thermal_zone_device_ops gadc_thermal_ops = {
.get_temp = gadc_thermal_get_temp,
};
+static const struct iio_chan_spec gadc_thermal_iio_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }
+};
+
+static int gadc_thermal_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct gadc_thermal_info *gtinfo = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = gadc_thermal_get_temp(gtinfo->tz_dev, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info gadc_thermal_iio_info = {
+ .read_raw = gadc_thermal_read_raw,
+};
+
+static int gadc_iio_register(struct device *dev, struct gadc_thermal_info *gti)
+{
+ struct gadc_thermal_info *gtinfo;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gtinfo));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ gtinfo = iio_priv(indio_dev);
+ memcpy(gtinfo, gti, sizeof(*gtinfo));
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->info = &gadc_thermal_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = gadc_thermal_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(gadc_thermal_iio_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
static int gadc_thermal_read_linear_lookup_table(struct device *dev,
struct gadc_thermal_info *gti)
{
@@ -153,7 +206,7 @@ static int gadc_thermal_probe(struct platform_device *pdev)
devm_thermal_add_hwmon_sysfs(dev, gti->tz_dev);
- return 0;
+ return gadc_iio_register(&pdev->dev, gti);
}
static const struct of_device_id of_adc_thermal_match[] = {
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 2328ac0d8561..2f4e2dc46b8f 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -41,6 +41,8 @@ static struct thermal_governor *def_governor;
static bool thermal_pm_suspended;
+static struct workqueue_struct *thermal_wq __ro_after_init;
+
/*
* Governor section: set of functions to handle thermal governors
*
@@ -313,7 +315,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
if (delay > HZ)
delay = round_jiffies_relative(delay);
- mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay);
+ mod_delayed_work(thermal_wq, &tz->poll_queue, delay);
}
static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
@@ -369,7 +371,8 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
tz->governor->update_tz(tz, reason);
}
-static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdown)
+static void thermal_zone_device_halt(struct thermal_zone_device *tz,
+ enum hw_protection_action action)
{
/*
* poweroff_delay_ms must be a carefully profiled positive value.
@@ -380,21 +383,23 @@ static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdo
dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type);
- if (shutdown)
- hw_protection_shutdown(msg, poweroff_delay_ms);
- else
- hw_protection_reboot(msg, poweroff_delay_ms);
+ __hw_protection_trigger(msg, poweroff_delay_ms, action);
}
void thermal_zone_device_critical(struct thermal_zone_device *tz)
{
- thermal_zone_device_halt(tz, true);
+ thermal_zone_device_halt(tz, HWPROT_ACT_DEFAULT);
}
EXPORT_SYMBOL(thermal_zone_device_critical);
+void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz)
+{
+ thermal_zone_device_halt(tz, HWPROT_ACT_SHUTDOWN);
+}
+
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz)
{
- thermal_zone_device_halt(tz, false);
+ thermal_zone_device_halt(tz, HWPROT_ACT_REBOOT);
}
static void handle_critical_trips(struct thermal_zone_device *tz,
@@ -497,7 +502,7 @@ void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
WRITE_ONCE(trip->hysteresis, hyst);
thermal_notify_tz_trip_change(tz, trip);
/*
- * If the zone temperature is above or at the trip tmperature, the trip
+ * If the zone temperature is above or at the trip temperature, the trip
* is in the trips_reached list and its threshold is equal to its low
* temperature. It needs to stay in that list, but its threshold needs
* to be updated and the list ordering may need to be restored.
@@ -690,18 +695,12 @@ int thermal_zone_device_disable(struct thermal_zone_device *tz)
}
EXPORT_SYMBOL_GPL(thermal_zone_device_disable);
-static bool thermal_zone_is_present(struct thermal_zone_device *tz)
-{
- return !list_empty(&tz->node);
-}
-
void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
guard(thermal_zone)(tz);
- if (thermal_zone_is_present(tz))
- __thermal_zone_device_update(tz, event);
+ __thermal_zone_device_update(tz, event);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
@@ -841,7 +840,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state)
return -EINVAL;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
@@ -858,7 +857,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
goto free_mem;
dev->id = result;
- sprintf(dev->name, "cdev%d", dev->id);
+ snprintf(dev->name, sizeof(dev->name), "cdev%d", dev->id);
result =
sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
if (result)
@@ -959,6 +958,8 @@ static void thermal_release(struct device *dev)
sizeof("thermal_zone") - 1)) {
tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz);
+ thermal_set_governor(tz, NULL);
+ ida_destroy(&tz->ida);
mutex_destroy(&tz->lock);
complete(&tz->removal);
} else if (!strncmp(dev_name(dev), "cooling_device",
@@ -971,7 +972,11 @@ static void thermal_release(struct device *dev)
}
}
-static struct class *thermal_class;
+static const struct class thermal_class = {
+ .name = "thermal",
+ .dev_release = thermal_release,
+};
+static bool thermal_class_unavailable __ro_after_init = true;
static inline
void print_bind_err_msg(struct thermal_zone_device *tz,
@@ -1040,7 +1045,7 @@ static void thermal_cooling_device_init_complete(struct thermal_cooling_device *
* @np: a pointer to a device tree node.
* @type: the thermal cooling device type.
* @devdata: device private data.
- * @ops: standard thermal cooling devices callbacks.
+ * @ops: standard thermal cooling devices callbacks.
*
* This interface function adds a new thermal cooling device (fan/processor/...)
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
@@ -1058,16 +1063,16 @@ __thermal_cooling_device_register(struct device_node *np,
{
struct thermal_cooling_device *cdev;
unsigned long current_state;
- int id, ret;
+ int ret;
if (!ops || !ops->get_max_state || !ops->get_cur_state ||
!ops->set_cur_state)
return ERR_PTR(-EINVAL);
- if (!thermal_class)
+ if (thermal_class_unavailable)
return ERR_PTR(-ENODEV);
- cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ cdev = kzalloc_obj(*cdev);
if (!cdev)
return ERR_PTR(-ENOMEM);
@@ -1075,7 +1080,6 @@ __thermal_cooling_device_register(struct device_node *np,
if (ret < 0)
goto out_kfree_cdev;
cdev->id = ret;
- id = ret;
cdev->type = kstrdup_const(type ? type : "", GFP_KERNEL);
if (!cdev->type) {
@@ -1088,7 +1092,7 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->np = np;
cdev->ops = ops;
cdev->updated = false;
- cdev->device.class = thermal_class;
+ cdev->device.class = &thermal_class;
cdev->devdata = devdata;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
@@ -1132,7 +1136,7 @@ out_cooling_dev:
out_cdev_type:
kfree_const(cdev->type);
out_ida_remove:
- ida_free(&thermal_cdev_ida, id);
+ ida_free(&thermal_cdev_ida, cdev->id);
out_kfree_cdev:
kfree(cdev);
return ERR_PTR(ret);
@@ -1502,15 +1506,19 @@ thermal_zone_device_register_with_trips(const char *type,
const struct thermal_trip *trip = trips;
struct thermal_zone_device *tz;
struct thermal_trip_desc *td;
+ size_t type_len = 0;
int id;
int result;
- if (!type || strlen(type) == 0) {
+ if (type)
+ type_len = strnlen(type, THERMAL_NAME_LENGTH);
+
+ if (type_len == 0) {
pr_err("No thermal zone type defined\n");
return ERR_PTR(-EINVAL);
}
- if (strlen(type) >= THERMAL_NAME_LENGTH) {
+ if (type_len == THERMAL_NAME_LENGTH) {
pr_err("Thermal zone name (%s) too long, should be under %d chars\n",
type, THERMAL_NAME_LENGTH);
return ERR_PTR(-EINVAL);
@@ -1532,10 +1540,10 @@ thermal_zone_device_register_with_trips(const char *type,
if (polling_delay && passive_delay > polling_delay)
return ERR_PTR(-EINVAL);
- if (!thermal_class)
+ if (thermal_class_unavailable)
return ERR_PTR(-ENODEV);
- tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL);
+ tz = kzalloc_flex(*tz, trips, num_trips);
if (!tz)
return ERR_PTR(-ENOMEM);
@@ -1568,7 +1576,7 @@ thermal_zone_device_register_with_trips(const char *type,
if (!tz->ops.critical)
tz->ops.critical = thermal_zone_device_critical;
- tz->device.class = thermal_class;
+ tz->device.class = &thermal_class;
tz->devdata = devdata;
tz->num_trips = num_trips;
for_each_trip_desc(tz, td) {
@@ -1589,26 +1597,28 @@ thermal_zone_device_register_with_trips(const char *type,
tz->state = TZ_STATE_FLAG_INIT;
- /* sys I/F */
- /* Add nodes that are always present via .groups */
- result = thermal_zone_create_device_groups(tz);
+ result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
if (result)
goto remove_id;
- result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
+ thermal_zone_device_init(tz);
+
+ result = thermal_zone_init_governor(tz);
+ if (result)
+ goto remove_id;
+
+ /* sys I/F */
+ /* Add nodes that are always present via .groups */
+ result = thermal_zone_create_device_groups(tz);
if (result) {
- thermal_zone_destroy_device_groups(tz);
+ thermal_set_governor(tz, NULL);
goto remove_id;
}
- thermal_zone_device_init(tz);
+
result = device_register(&tz->device);
if (result)
goto release_device;
- result = thermal_zone_init_governor(tz);
- if (result)
- goto unregister;
-
if (!tz->tzp || !tz->tzp->no_hwmon) {
result = thermal_add_hwmon_sysfs(tz);
if (result)
@@ -1633,6 +1643,7 @@ unregister:
device_del(&tz->device);
release_device:
put_device(&tz->device);
+ wait_for_completion(&tz->removal);
remove_id:
ida_free(&thermal_tz_ida, id);
free_tzp:
@@ -1715,12 +1726,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
cancel_delayed_work_sync(&tz->poll_queue);
- thermal_set_governor(tz, NULL);
-
thermal_thresholds_exit(tz);
thermal_remove_hwmon_sysfs(tz);
- ida_free(&thermal_tz_ida, tz->id);
- ida_destroy(&tz->ida);
device_del(&tz->device);
put_device(&tz->device);
@@ -1728,6 +1735,9 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
thermal_notify_tz_delete(tz);
wait_for_completion(&tz->removal);
+
+ ida_free(&thermal_tz_ida, tz->id);
+
kfree(tz->tzp);
kfree(tz);
}
@@ -1778,6 +1788,10 @@ static void thermal_zone_device_resume(struct work_struct *work)
guard(thermal_zone)(tz);
+ /* If the thermal zone is going away, there's nothing to do. */
+ if (tz->state & TZ_STATE_FLAG_EXIT)
+ return;
+
tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING);
thermal_debug_tz_resume(tz);
@@ -1804,9 +1818,12 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz)
}
tz->state |= TZ_STATE_FLAG_SUSPENDED;
+
+ /* Prevent new work from getting to the workqueue subsequently. */
+ cancel_delayed_work(&tz->poll_queue);
}
-static void thermal_pm_notify_prepare(void)
+static void __thermal_pm_prepare(void)
{
struct thermal_zone_device *tz;
@@ -1818,12 +1835,23 @@ static void thermal_pm_notify_prepare(void)
thermal_zone_pm_prepare(tz);
}
+void thermal_pm_prepare(void)
+{
+ if (thermal_class_unavailable)
+ return;
+
+ __thermal_pm_prepare();
+ /*
+ * Allow any leftover thermal work items already on the worqueue to
+ * complete so they don't get in the way later.
+ */
+ flush_workqueue(thermal_wq);
+}
+
static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
{
guard(thermal_zone)(tz);
- cancel_delayed_work(&tz->poll_queue);
-
reinit_completion(&tz->resume);
tz->state |= TZ_STATE_FLAG_RESUMING;
@@ -1833,13 +1861,16 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
*/
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume);
/* Queue up the work without a delay. */
- mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0);
+ mod_delayed_work(thermal_wq, &tz->poll_queue, 0);
}
-static void thermal_pm_notify_complete(void)
+void thermal_pm_complete(void)
{
struct thermal_zone_device *tz;
+ if (thermal_class_unavailable)
+ return;
+
guard(mutex)(&thermal_list_lock);
thermal_pm_suspended = false;
@@ -1848,36 +1879,6 @@ static void thermal_pm_notify_complete(void)
thermal_zone_pm_complete(tz);
}
-static int thermal_pm_notify(struct notifier_block *nb,
- unsigned long mode, void *_unused)
-{
- switch (mode) {
- case PM_HIBERNATION_PREPARE:
- case PM_RESTORE_PREPARE:
- case PM_SUSPEND_PREPARE:
- thermal_pm_notify_prepare();
- break;
- case PM_POST_HIBERNATION:
- case PM_POST_RESTORE:
- case PM_POST_SUSPEND:
- thermal_pm_notify_complete();
- break;
- default:
- break;
- }
- return 0;
-}
-
-static struct notifier_block thermal_pm_nb = {
- .notifier_call = thermal_pm_notify,
- /*
- * Run at the lowest priority to avoid interference between the thermal
- * zone resume work items spawned by thermal_pm_notify() and the other
- * PM notifiers.
- */
- .priority = INT_MIN,
-};
-
static int __init thermal_init(void)
{
int result;
@@ -1888,35 +1889,28 @@ static int __init thermal_init(void)
if (result)
goto error;
- result = thermal_register_governors();
- if (result)
- goto unregister_netlink;
-
- thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL);
- if (!thermal_class) {
+ thermal_wq = alloc_workqueue("thermal_events", WQ_POWER_EFFICIENT, 0);
+ if (!thermal_wq) {
result = -ENOMEM;
- goto unregister_governors;
+ goto unregister_netlink;
}
- thermal_class->name = "thermal";
- thermal_class->dev_release = thermal_release;
+ result = thermal_register_governors();
+ if (result)
+ goto destroy_workqueue;
- result = class_register(thermal_class);
- if (result) {
- kfree(thermal_class);
- thermal_class = NULL;
+ result = class_register(&thermal_class);
+ if (result)
goto unregister_governors;
- }
- result = register_pm_notifier(&thermal_pm_nb);
- if (result)
- pr_warn("Thermal: Can not register suspend notifier, return %d\n",
- result);
+ thermal_class_unavailable = false;
return 0;
unregister_governors:
thermal_unregister_governors();
+destroy_workqueue:
+ destroy_workqueue(thermal_wq);
unregister_netlink:
thermal_netlink_exit();
error:
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 09866f0ce765..d3acff602f9c 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -77,6 +77,7 @@ struct thermal_governor {
* @device: &struct device for this thermal zone
* @removal: removal completion
* @resume: resume completion
+ * @trips_attribute_group: trip point sysfs attributes
* @trips_high: trips above the current zone temperature
* @trips_reached: trips below or at the current zone temperature
* @trips_invalid: trips with invalid temperature
@@ -97,9 +98,9 @@ struct thermal_governor {
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
* @prev_low_trip: the low current temperature if you've crossed a passive
- trip point.
+ * trip point.
* @prev_high_trip: the above current temperature if you've crossed a
- passive trip point.
+ * passive trip point.
* @ops: operations this &thermal_zone_device supports
* @tzp: thermal zone parameters
* @governor: pointer to the governor for this thermal zone
@@ -111,6 +112,8 @@ struct thermal_governor {
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @state: current state of the thermal zone
+ * @debugfs: this thermal zone device's thermal zone debug info
+ * @user_thresholds: list of userspace thresholds for temp. limit notifications
* @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
@@ -262,6 +265,7 @@ int thermal_build_list_of_policies(char *buf);
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event);
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz);
+void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz);
void thermal_governor_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c
index c800504c3cfe..da02d8f179b0 100644
--- a/drivers/thermal/thermal_debugfs.c
+++ b/drivers/thermal/thermal_debugfs.c
@@ -193,7 +193,7 @@ static struct thermal_debugfs *thermal_debugfs_add_id(struct dentry *d, int id)
struct thermal_debugfs *thermal_dbg;
char ids[IDSLENGTH];
- thermal_dbg = kzalloc(sizeof(*thermal_dbg), GFP_KERNEL);
+ thermal_dbg = kzalloc_obj(*thermal_dbg);
if (!thermal_dbg)
return NULL;
@@ -226,7 +226,7 @@ thermal_debugfs_cdev_record_alloc(struct thermal_debugfs *thermal_dbg,
{
struct cdev_record *cdev_record;
- cdev_record = kzalloc(sizeof(*cdev_record), GFP_KERNEL);
+ cdev_record = kzalloc_obj(*cdev_record);
if (!cdev_record)
return NULL;
@@ -319,7 +319,7 @@ static int cdev_tt_seq_show(struct seq_file *s, void *v)
int i = *(loff_t *)v;
if (!i)
- seq_puts(s, "Transition\tOccurences\n");
+ seq_puts(s, "Transition\tOccurrences\n");
list_for_each_entry(entry, &transitions[i], node) {
/*
@@ -559,7 +559,7 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev
struct tz_episode *tze;
int i;
- tze = kzalloc(struct_size(tze, trip_stats, tz->num_trips), GFP_KERNEL);
+ tze = kzalloc_flex(*tze, trip_stats, tz->num_trips);
if (!tze)
return NULL;
@@ -807,7 +807,7 @@ static int tze_seq_show(struct seq_file *s, void *v)
seq_printf(s, ",-Mitigation at %llums, duration%c%llums, max. temp=%dm°C\n",
ktime_to_ms(tze->timestamp), c, duration_ms, tze->max_temp);
- seq_printf(s, "| trip | type | temp(m°C) | hyst(m°C) | duration(ms) | avg(m°C) | min(m°C) |\n");
+ seq_puts(s, "| trip | type | temp(m°C) | hyst(m°C) | duration(ms) | avg(m°C) | min(m°C) |\n");
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
@@ -876,7 +876,7 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz)
tz_dbg->tz = tz;
- tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL);
+ tz_dbg->trips_crossed = kzalloc_objs(int, tz->num_trips);
if (!tz_dbg->trips_crossed) {
thermal_debugfs_remove_id(thermal_dbg);
return;
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index 0ecccd4d8556..b624892bc6d6 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -63,7 +63,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
if (ret)
return ret;
- return sprintf(buf, "%d\n", temperature);
+ return sysfs_emit(buf, "%d\n", temperature);
}
static ssize_t
@@ -84,7 +84,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
if (ret)
return ret;
- return sprintf(buf, "%d\n", temperature);
+ return sysfs_emit(buf, "%d\n", temperature);
}
@@ -96,7 +96,7 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
mutex_lock(&thermal_hwmon_list_lock);
list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
- strcpy(type, tz->type);
+ strscpy(type, tz->type);
strreplace(type, '-', '_');
if (!strcmp(hwmon->type, type)) {
mutex_unlock(&thermal_hwmon_list_lock);
@@ -145,7 +145,7 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
goto register_sys_interface;
}
- hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);
+ hwmon = kzalloc_obj(*hwmon);
if (!hwmon)
return -ENOMEM;
@@ -160,7 +160,7 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
}
register_sys_interface:
- temp = kzalloc(sizeof(*temp), GFP_KERNEL);
+ temp = kzalloc_obj(*temp);
if (!temp) {
result = -ENOMEM;
goto unregister_name;
diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
index 5401f03d6b6c..99085c806a1f 100644
--- a/drivers/thermal/thermal_of.c
+++ b/drivers/thermal/thermal_of.c
@@ -107,7 +107,7 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n
if (!count)
return NULL;
- struct thermal_trip *tt __free(kfree) = kzalloc(sizeof(*tt) * count, GFP_KERNEL);
+ struct thermal_trip *tt __free(kfree) = kzalloc_objs(*tt, count);
if (!tt)
return ERR_PTR(-ENOMEM);
@@ -144,7 +144,7 @@ static struct device_node *of_thermal_zone_find(struct device_node *sensor, int
count = of_count_phandle_with_args(child, "thermal-sensors",
"#thermal-sensor-cells");
if (count <= 0) {
- pr_err("%pOFn: missing thermal sensor\n", child);
+ pr_err("%pOFP: missing thermal sensor\n", child);
return ERR_PTR(-EINVAL);
}
@@ -156,14 +156,14 @@ static struct device_node *of_thermal_zone_find(struct device_node *sensor, int
"#thermal-sensor-cells",
i, &sensor_specs);
if (ret < 0) {
- pr_err("%pOFn: Failed to read thermal-sensors cells: %d\n", child, ret);
+ pr_err("%pOFP: Failed to read thermal-sensors cells: %d\n", child, ret);
return ERR_PTR(ret);
}
of_node_put(sensor_specs.np);
if ((sensor == sensor_specs.np) && id == (sensor_specs.args_count ?
sensor_specs.args[0] : 0)) {
- pr_debug("sensor %pOFn id=%d belongs to %pOFn\n", sensor, id, child);
+ pr_debug("sensor %pOFP id=%d belongs to %pOFP\n", sensor, id, child);
return no_free_ptr(child);
}
}
@@ -180,7 +180,7 @@ static int thermal_of_monitor_init(struct device_node *np, int *delay, int *pdel
if (ret == -EINVAL) {
*pdelay = 0;
} else if (ret < 0) {
- pr_err("%pOFn: Couldn't get polling-delay-passive: %d\n", np, ret);
+ pr_err("%pOFP: Couldn't get polling-delay-passive: %d\n", np, ret);
return ret;
}
@@ -188,7 +188,7 @@ static int thermal_of_monitor_init(struct device_node *np, int *delay, int *pdel
if (ret == -EINVAL) {
*delay = 0;
} else if (ret < 0) {
- pr_err("%pOFn: Couldn't get polling-delay: %d\n", np, ret);
+ pr_err("%pOFP: Couldn't get polling-delay: %d\n", np, ret);
return ret;
}
@@ -280,10 +280,10 @@ static bool thermal_of_cm_lookup(struct device_node *cm_np,
struct cooling_spec *c)
{
for_each_child_of_node_scoped(cm_np, child) {
- struct device_node *tr_np;
int count, i;
- tr_np = of_parse_phandle(child, "trip", 0);
+ struct device_node *tr_np __free(device_node) =
+ of_parse_phandle(child, "trip", 0);
if (tr_np != trip->priv)
continue;
@@ -380,23 +380,23 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
np = of_thermal_zone_find(sensor, id);
if (IS_ERR(np)) {
if (PTR_ERR(np) != -ENODEV)
- pr_err("Failed to find thermal zone for %pOFn id=%d\n", sensor, id);
+ pr_err("Failed to find thermal zone for %pOFP id=%d\n", sensor, id);
return ERR_CAST(np);
}
trips = thermal_of_trips_init(np, &ntrips);
if (IS_ERR(trips)) {
- pr_err("Failed to parse trip points for %pOFn id=%d\n", sensor, id);
+ pr_err("Failed to parse trip points for %pOFP id=%d\n", sensor, id);
ret = PTR_ERR(trips);
goto out_of_node_put;
}
if (!trips)
- pr_info("No trip points found for %pOFn id=%d\n", sensor, id);
+ pr_info("No trip points found for %pOFP id=%d\n", sensor, id);
ret = thermal_of_monitor_init(np, &delay, &pdelay);
if (ret) {
- pr_err("Failed to initialize monitoring delays from %pOFn\n", np);
+ pr_err("Failed to initialize monitoring delays from %pOFP\n", np);
goto out_kfree_trips;
}
@@ -405,16 +405,19 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
of_ops.should_bind = thermal_of_should_bind;
ret = of_property_read_string(np, "critical-action", &action);
- if (!ret)
- if (!of_ops.critical && !strcasecmp(action, "reboot"))
+ if (!ret && !of_ops.critical) {
+ if (!strcasecmp(action, "reboot"))
of_ops.critical = thermal_zone_device_critical_reboot;
+ else if (!strcasecmp(action, "shutdown"))
+ of_ops.critical = thermal_zone_device_critical_shutdown;
+ }
tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips,
data, &of_ops, &tzp,
pdelay, delay);
if (IS_ERR(tz)) {
ret = PTR_ERR(tz);
- pr_err("Failed to register thermal zone %pOFn: %d\n", np, ret);
+ pr_err("Failed to register thermal zone %pOFP: %d\n", np, ret);
goto out_kfree_trips;
}
diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
index 24b9055a0b6c..5eecae13f07d 100644
--- a/drivers/thermal/thermal_sysfs.c
+++ b/drivers/thermal/thermal_sysfs.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/string_choices.h>
#include <linux/jiffies.h>
#include "thermal_core.h"
@@ -29,7 +30,7 @@ type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
- return sprintf(buf, "%s\n", tz->type);
+ return sysfs_emit(buf, "%s\n", tz->type);
}
static ssize_t
@@ -40,10 +41,13 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf)
ret = thermal_zone_get_temp(tz, &temperature);
- if (ret)
- return ret;
+ if (!ret)
+ return sysfs_emit(buf, "%d\n", temperature);
- return sprintf(buf, "%d\n", temperature);
+ if (ret == -EAGAIN)
+ return -ENODATA;
+
+ return ret;
}
static ssize_t
@@ -53,10 +57,8 @@ mode_show(struct device *dev, struct device_attribute *attr, char *buf)
guard(thermal_zone)(tz);
- if (tz->mode == THERMAL_DEVICE_ENABLED)
- return sprintf(buf, "enabled\n");
-
- return sprintf(buf, "disabled\n");
+ return sysfs_emit(buf, "%s\n",
+ str_enabled_disabled(tz->mode == THERMAL_DEVICE_ENABLED));
}
static ssize_t
@@ -94,7 +96,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
- return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type));
+ return sysfs_emit(buf, "%s\n", thermal_trip_type_name(trip->type));
}
static ssize_t
@@ -139,7 +141,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
- return sprintf(buf, "%d\n", READ_ONCE(trip->temperature));
+ return sysfs_emit(buf, "%d\n", READ_ONCE(trip->temperature));
}
static ssize_t
@@ -185,7 +187,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
- return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis));
+ return sysfs_emit(buf, "%d\n", READ_ONCE(trip->hysteresis));
}
static ssize_t
@@ -196,7 +198,7 @@ policy_store(struct device *dev, struct device_attribute *attr,
char name[THERMAL_NAME_LENGTH];
int ret;
- snprintf(name, sizeof(name), "%s", buf);
+ strscpy(name, buf);
ret = thermal_zone_device_set_policy(tz, name);
if (!ret)
@@ -210,7 +212,7 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
- return sprintf(buf, "%s\n", tz->governor->name);
+ return sysfs_emit(buf, "%s\n", tz->governor->name);
}
static ssize_t
@@ -257,7 +259,7 @@ sustainable_power_show(struct device *dev, struct device_attribute *devattr,
struct thermal_zone_device *tz = to_thermal_zone(dev);
if (tz->tzp)
- return sprintf(buf, "%u\n", tz->tzp->sustainable_power);
+ return sysfs_emit(buf, "%u\n", tz->tzp->sustainable_power);
else
return -EIO;
}
@@ -288,7 +290,7 @@ sustainable_power_store(struct device *dev, struct device_attribute *devattr,
struct thermal_zone_device *tz = to_thermal_zone(dev); \
\
if (tz->tzp) \
- return sprintf(buf, "%d\n", tz->tzp->name); \
+ return sysfs_emit(buf, "%d\n", tz->tzp->name); \
else \
return -EIO; \
} \
@@ -389,7 +391,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
struct attribute **attrs;
int i;
- attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL);
+ attrs = kzalloc_objs(*attrs, tz->num_trips * 3 + 1);
if (!attrs)
return -ENOMEM;
@@ -462,7 +464,7 @@ int thermal_zone_create_device_groups(struct thermal_zone_device *tz)
/* we need one extra for trips and the NULL to terminate the array */
size = ARRAY_SIZE(thermal_zone_attribute_groups) + 2;
/* This also takes care of API requirement to be NULL terminated */
- groups = kcalloc(size, sizeof(*groups), GFP_KERNEL);
+ groups = kzalloc_objs(*groups, size);
if (!groups)
return -ENOMEM;
@@ -502,7 +504,7 @@ cdev_type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
- return sprintf(buf, "%s\n", cdev->type);
+ return sysfs_emit(buf, "%s\n", cdev->type);
}
static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
@@ -510,7 +512,7 @@ static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
- return sprintf(buf, "%ld\n", cdev->max_state);
+ return sysfs_emit(buf, "%ld\n", cdev->max_state);
}
static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
@@ -523,7 +525,7 @@ static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
ret = cdev->ops->get_cur_state(cdev, &state);
if (ret)
return ret;
- return sprintf(buf, "%ld\n", state);
+ return sysfs_emit(buf, "%ld\n", state);
}
static ssize_t
@@ -635,7 +637,7 @@ static ssize_t total_trans_show(struct device *dev,
return 0;
spin_lock(&stats->lock);
- ret = sprintf(buf, "%u\n", stats->total_trans);
+ ret = sysfs_emit(buf, "%u\n", stats->total_trans);
spin_unlock(&stats->lock);
return ret;
@@ -661,8 +663,8 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
update_time_in_state(stats);
for (i = 0; i <= cdev->max_state; i++) {
- len += sprintf(buf + len, "state%u\t%llu\n", i,
- ktime_to_ms(stats->time_in_state[i]));
+ len += sysfs_emit_at(buf, len, "state%u\t%llu\n", i,
+ ktime_to_ms(stats->time_in_state[i]));
}
spin_unlock(&stats->lock);
@@ -843,7 +845,7 @@ trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)
instance = container_of(attr, struct thermal_instance, attr);
- return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip));
+ return sysfs_emit(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip));
}
ssize_t
@@ -853,7 +855,7 @@ weight_show(struct device *dev, struct device_attribute *attr, char *buf)
instance = container_of(attr, struct thermal_instance, weight_attr);
- return sprintf(buf, "%d\n", instance->weight);
+ return sysfs_emit(buf, "%d\n", instance->weight);
}
ssize_t weight_store(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c
index 38f5fd0e8930..970b8fd33847 100644
--- a/drivers/thermal/thermal_thresholds.c
+++ b/drivers/thermal/thermal_thresholds.c
@@ -181,7 +181,7 @@ int thermal_thresholds_add(struct thermal_zone_device *tz,
t->direction |= direction;
} else {
- t = kmalloc(sizeof(*t), GFP_KERNEL);
+ t = kmalloc_obj(*t);
if (!t)
return -ENOMEM;