diff options
Diffstat (limited to 'drivers/thermal')
21 files changed, 888 insertions, 51 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index d3f9686e26e7..a09c188b9ad1 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 @@ -327,6 +327,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..d7718978db24 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 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/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c index 7fbba2233c4c..685a5aee5e0d 100644 --- a/drivers/thermal/broadcom/bcm2835_thermal.c +++ b/drivers/thermal/broadcom/bcm2835_thermal.c @@ -192,7 +192,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/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile index fe3f43924525..184318d1792b 100644 --- a/drivers/thermal/intel/int340x_thermal/Makefile +++ b/drivers/thermal/intel/int340x_thermal/Makefile @@ -9,6 +9,7 @@ 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 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..2d6504514893 --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c @@ -0,0 +1,243 @@ +// 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/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 3 + +struct ptc_data { + u32 offset; + struct attribute_group ptc_attr_group; + struct attribute *ptc_attrs[PTC_MAX_ATTRS]; + struct device_attribute temperature_target_attr; + struct device_attribute enable_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", + 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); + +#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); + + data->ptc_attrs[index++] = &data->temperature_target_attr.attr; + data->ptc_attrs[index++] = &data->enable_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]; + +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_create_groups(pdev, i, &ptc_instance[i]); + } + } + + 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); + } +} +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..29fcece48cad 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/pci.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" @@ -153,7 +154,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; @@ -399,13 +400,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; } } @@ -427,6 +436,8 @@ int proc_thermal_mmio_add(struct pci_dev *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 +450,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..9a6ca43b6fa2 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h @@ -67,6 +67,7 @@ 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 #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 +124,6 @@ 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); #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 2097aae39946..00160936070a 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c @@ -486,7 +486,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { 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_DVFS | - PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) }, + 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) }, @@ -497,7 +498,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT | - PROC_THERMAL_FEATURE_POWER_FLOOR) }, + PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) }, { }, }; diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 5b18a46a10b0..bd2fca7dc017 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -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; diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index 96a24df79686..9a4cec000910 100644 --- a/drivers/thermal/intel/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -340,7 +340,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 +356,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_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c index 9ff0ebdde0ef..f352ecafbedf 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) @@ -81,14 +82,14 @@ static int __init tcc_cooling_init(void) 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; diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index e69868e868eb..debc94e2dc16 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -273,7 +273,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 +287,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 +643,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 +654,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 +669,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 2841d14914b7..3fc679b6f11b 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" diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 088481d91e6e..985925147ac0 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -213,6 +213,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 +252,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,8 +263,6 @@ 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) @@ -1374,8 +1374,6 @@ 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 mt7988_lvts_ap_data_ctrl[] = { diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c index d2d49264cf83..991d1573983d 100644 --- a/drivers/thermal/qcom/lmh.c +++ b/drivers/thermal/qcom/lmh.c @@ -209,7 +209,8 @@ static int lmh_probe(struct platform_device *pdev) } 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(of_fwnode_handle(np), 1, &lmh_irq_ops, + lmh_data); if (!lmh_data->domain) { dev_err(dev, "Error adding irq_domain\n"); return -EINVAL; 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.c b/drivers/thermal/qcom/tsens.c index 1f5d4de017d9..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) @@ -975,10 +975,16 @@ int __init init_common(struct tsens_priv *priv) ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); if (ret) goto err_put_device; - if (!enabled && (tsens_version(priv) != VER_2_X_NO_RPM)) { - dev_err(dev, "%s: device not enabled\n", __func__); - ret = -ENODEV; - goto err_put_device; + if (!enabled) { + 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,9 @@ 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, }, { diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 336bc868fd7c..2a7afa4c899b 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -34,6 +34,7 @@ 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, }; @@ -651,6 +652,9 @@ 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; diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 2c5ddf0db40c..926f1052e6de 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1234,7 +1234,7 @@ 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, + soc_irq_cdata.domain = irq_domain_create_linear(of_fwnode_handle(np), num_irqs, &soctherm_oc_domain_ops, &soc_irq_cdata); |