From 53529b1d93e492fe8ce7c0b7c84af9efae7f4ad9 Mon Sep 17 00:00:00 2001 From: Kapil Porwal Date: Mon, 9 Mar 2026 18:01:37 +0530 Subject: [PATCH] soc/qualcomm/common: Add Qualcomm TSENS support Introduce a generic driver for the Qualcomm Temperature Sensor (TSENS) V2 hardware block. This driver provides the infrastructure to read temperature data from hardware status registers and monitor them against software-defined thresholds. The driver sign-extends the 12-bit raw temperature values and scales the output to millidegrees Celsius for accurate monitoring. TEST=Verify all x1p42100 thermal zones are readable on Google/Quartz. Change-Id: I826df3f86edc30ac57d84f672b487a8b9b51728a Signed-off-by: Kapil Porwal Reviewed-on: https://review.coreboot.org/c/coreboot/+/91609 Reviewed-by: Subrata Banik Tested-by: build bot (Jenkins) --- src/soc/qualcomm/common/Kconfig | 7 ++ .../qualcomm/common/include/soc/qcom_tsens.h | 41 +++++++++++ src/soc/qualcomm/common/tsens.c | 72 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 src/soc/qualcomm/common/include/soc/qcom_tsens.h create mode 100644 src/soc/qualcomm/common/tsens.c diff --git a/src/soc/qualcomm/common/Kconfig b/src/soc/qualcomm/common/Kconfig index 98760cec63..373ba51c40 100644 --- a/src/soc/qualcomm/common/Kconfig +++ b/src/soc/qualcomm/common/Kconfig @@ -54,4 +54,11 @@ config SOC_QUALCOMM_QCLIB_SKIP_MMU_TOGGLE flushes and TLB invalidations associated with mmu_disable() and mmu_enable() calls. +config SOC_QUALCOMM_DEBUG_TSENS + bool + default n + help + When enabled, a call to monitor TSENS will dump the sensor data on + the debug console. + endif diff --git a/src/soc/qualcomm/common/include/soc/qcom_tsens.h b/src/soc/qualcomm/common/include/soc/qcom_tsens.h new file mode 100644 index 0000000000..6bb82a5e30 --- /dev/null +++ b/src/soc/qualcomm/common/include/soc/qcom_tsens.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _SOC_QUALCOMM_TSENS_H_ +#define _SOC_QUALCOMM_TSENS_H_ + +#include +#include +#include + +#define TM_SN_STATUS_OFF 0x00a0 +#define SN_STATUS_VALID (1 << 21) +#define SN_STATUS_TEMP_MASK 0xFFF + +struct tsens_controller { + const char *name; + uintptr_t tm_base; + uintptr_t srot_base; + uint32_t sensor_count; +}; + +enum sensor_type { + TYPE_TSENS +}; + +struct thermal_zone_map { + const char *label; + enum sensor_type type; + const void *ctrl; + int hw_id; + int threshold; +}; + +/* + * Iterates through all defined zones and checks thresholds. + */ +void qcom_tsens_monitor_all(bool *has_crossed_threshold); + +/* External Data (Defined in SoC file) */ +extern const struct thermal_zone_map qcom_thermal_zones[]; + +#endif /* _SOC_QUALCOMM_TSENS_H_ */ diff --git a/src/soc/qualcomm/common/tsens.c b/src/soc/qualcomm/common/tsens.c new file mode 100644 index 0000000000..12f5ecdc5b --- /dev/null +++ b/src/soc/qualcomm/common/tsens.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include + +#if CONFIG(SOC_QUALCOMM_DEBUG_TSENS) +#define thermal_printk(fmt, ...) printk(BIOS_INFO, fmt, ##__VA_ARGS__) +#else +#define thermal_printk(fmt, ...) do { (void)(fmt); } while (0) +#endif + +static int read_tsens(const struct thermal_zone_map *zone, int *out) +{ + if (!zone) + return -1; + + const struct tsens_controller *ctrl = (const struct tsens_controller *)zone->ctrl; + if (!ctrl || (zone->hw_id >= (int)ctrl->sensor_count)) + return -1; + + uintptr_t status_addr = ctrl->tm_base + TM_SN_STATUS_OFF + (zone->hw_id * 4); + + uint32_t status = read32p(status_addr); + if (!(status & SN_STATUS_VALID)) + return -2; + + /* Sign extend 12-bit temperature and scale to milli C */ + *out = ((int32_t)((status & SN_STATUS_TEMP_MASK) << 20) >> 20) * 100; + return 0; +} + +void qcom_tsens_monitor_all(bool *has_crossed_threshold) +{ + if (!has_crossed_threshold) + return; + + *has_crossed_threshold = false; + + thermal_printk("\n------------------ Thermal Sensor State --------------------\n"); + thermal_printk("%-15s | %-10s | %-8s | %-10s\n", "Zone", "Type", "Temp (C)", "Status"); + thermal_printk("------------------------------------------------------------\n"); + + for (const struct thermal_zone_map *zone = qcom_thermal_zones; zone->label != NULL; zone++) { + int data = 0, status = -1; + const char *type_str = "UNK"; + (void)type_str; + + if (zone->type == TYPE_TSENS) { + type_str = "SoC"; + status = read_tsens(zone, &data); + } + + if (status == 0) { + thermal_printk("%-15s | %-10s | %3d.%1d | ", + zone->label, type_str, data / 1000, (data % 1000) / 100); + + if (data >= zone->threshold) { + *has_crossed_threshold = true; + thermal_printk("EXCEEDED\n"); + if (!CONFIG(SOC_QUALCOMM_DEBUG_TSENS)) + return; + } else { + thermal_printk("OK\n"); + } + } else { + thermal_printk("%-15s | %-10s | [ERR %d] | -\n", zone->label, type_str, status); + } + } +}