mac80211: ath12k: backport thermal sensor support

This is nearly identical to what landed in ath-next for v7.1, aside from
resolving a couple conflicts. A separate patch has been added to replace
CONFIG_THERMAL with CPTCFG_ATH12K_THERMAL so the setting may be enabled
via menuconfig (as is done with ath10k and ath11k).

Note that at this stage, throttling has not been implemented upstream,
hence the slight change in wording versus existing options.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1

Link: https://patch.msgid.link/20260223132622.43464-1-maharaja.kennadyrajan@oss.qualcomm.com
Signed-off-by: Matt Merhar <mattmerhar@protonmail.com>
Link: https://github.com/openwrt/openwrt/pull/22280
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Matt Merhar 2026-03-05 22:05:40 -05:00 committed by Robert Marko
parent a654fa7fe4
commit d85a332831
5 changed files with 457 additions and 6 deletions

View file

@ -13,6 +13,7 @@ PKG_CONFIG_DEPENDS += \
CONFIG_ATH10K_LEDS \
CONFIG_ATH10K_THERMAL \
CONFIG_ATH11K_THERMAL \
CONFIG_ATH12K_THERMAL \
CONFIG_ATH_USER_REGD
ifdef CONFIG_PACKAGE_MAC80211_DEBUGFS
@ -59,6 +60,7 @@ config-$(CONFIG_ATH9K_UBNTHSR) += ATH9K_UBNTHSR
config-$(CONFIG_ATH10K_LEDS) += ATH10K_LEDS
config-$(CONFIG_ATH10K_THERMAL) += ATH10K_THERMAL
config-$(CONFIG_ATH11K_THERMAL) += ATH11K_THERMAL
config-$(CONFIG_ATH12K_THERMAL) += ATH12K_THERMAL
config-$(call config_package,ath9k-htc) += ATH9K_HTC
config-$(call config_package,ath10k,regular) += ATH10K ATH10K_PCI
@ -368,7 +370,8 @@ define KernelPackage/ath12k
URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath12k
DEPENDS+= @PCI_SUPPORT +kmod-ath +@DRIVER_11AC_SUPPORT +@DRIVER_11AX_SUPPORT \
+kmod-crypto-michael-mic +kmod-qrtr-mhi \
+kmod-qcom-qmi-helpers +@DRIVER_11BE_SUPPORT
+kmod-qcom-qmi-helpers +@DRIVER_11BE_SUPPORT \
+ATH12K_THERMAL:kmod-hwmon-core +ATH12K_THERMAL:kmod-thermal
FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath12k/ath12k.ko
AUTOLOAD:=$(call AutoProbe,ath12k)
endef
@ -378,6 +381,14 @@ This module adds support for Qualcomm Technologies 802.11be family of
chipsets with PCI bus.
endef
define KernelPackage/ath12k/config
config ATH12K_THERMAL
bool "Enable ath12k thermal sensor support"
depends on PACKAGE_kmod-ath12k
endef
define KernelPackage/carl9170
$(call KernelPackage/mac80211/Default)
TITLE:=Driver for Atheros AR9170 USB sticks

View file

@ -22,10 +22,8 @@ This patch:
This targets monitor capture length correctness only and keeps the fix scoped
to the monitor RX delivery path.
Index: backports-6.18.7/drivers/net/wireless/ath/ath11k/dp_rx.c
===================================================================
--- backports-6.18.7.orig/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ backports-6.18.7/drivers/net/wireless/ath/ath11k/dp_rx.c
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -4931,8 +4931,13 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11
}

View file

@ -0,0 +1,380 @@
From 151322bccdbdb132f5a73cc8ad5d3ab89b90ed52 Mon Sep 17 00:00:00 2001
From: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
Date: Mon, 23 Feb 2026 18:56:22 +0530
Subject: wifi: ath12k: add basic hwmon temperature reporting
Add initial thermal support by wiring up a per-radio (pdev) hwmon temperature
sensor backed by the existing WMI pdev temperature command and event.
When userspace reads the sysfs file temp1_input, the driver sends
WMI_PDEV_GET_TEMPERATURE_CMDID (tag WMI_TAG_PDEV_GET_TEMPERATURE_CMD) and waits
for the corresponding WMI_PDEV_TEMPERATURE_EVENTID
(tag WMI_TAG_PDEV_TEMPERATURE_EVENT) to get the temperature and pdev_id.
Export the reported value in millidegrees Celsius as required by hwmon.
The temperature reported is per-radio (pdev). In a multi-radio wiphy under a
single phy, a separate hwmon device is created for each radio.
Sample command and output:
$ cat /sys/devices/pci0000:00/.../ieee80211/phyX/hwmonY/temp1_input
$ 50000
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Co-developed-by: Aishwarya R <aishwarya.r@oss.qualcomm.com>
Signed-off-by: Aishwarya R <aishwarya.r@oss.qualcomm.com>
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20260223132622.43464-1-maharaja.kennadyrajan@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/Makefile | 1 +
drivers/net/wireless/ath/ath12k/core.c | 13 ++++
drivers/net/wireless/ath/ath12k/core.h | 3 +
drivers/net/wireless/ath/ath12k/mac.c | 4 +
drivers/net/wireless/ath/ath12k/thermal.c | 124 ++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/thermal.h | 40 ++++++++++
drivers/net/wireless/ath/ath12k/wmi.c | 57 +++++++-------
7 files changed, 211 insertions(+), 31 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath12k/thermal.c
create mode 100644 drivers/net/wireless/ath/ath12k/thermal.h
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -857,11 +857,22 @@ static int ath12k_core_pdev_create(struc
return ret;
}
+ ret = ath12k_thermal_register(ab);
+ if (ret) {
+ ath12k_err(ab, "could not register thermal device: %d\n", ret);
+ goto err_dp_pdev_free;
+ }
+
return 0;
+
+err_dp_pdev_free:
+ ath12k_dp_pdev_free(ab);
+ return ret;
}
static void ath12k_core_pdev_destroy(struct ath12k_base *ab)
{
+ ath12k_thermal_unregister(ab);
ath12k_dp_pdev_free(ab);
}
@@ -1350,6 +1361,7 @@ static int ath12k_core_reconfigure_on_cr
int ret, total_vdev;
mutex_lock(&ab->core_lock);
+ ath12k_thermal_unregister(ab);
ath12k_dp_pdev_free(ab);
ath12k_ce_cleanup_pipes(ab);
ath12k_wmi_detach(ab);
@@ -1491,6 +1503,7 @@ static void ath12k_core_pre_reconfigure_
complete(&ar->vdev_delete_done);
complete(&ar->bss_survey_done);
complete_all(&ar->regd_update_completed);
+ complete_all(&ar->thermal.wmi_sync);
wake_up(&ar->dp.tx_empty_waitq);
idr_for_each(&ar->txmgmt_idr,
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -34,6 +34,7 @@
#include "wow.h"
#include "debugfs_htt_stats.h"
#include "coredump.h"
+#include "thermal.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -846,6 +847,8 @@ struct ath12k {
s8 max_allowed_tx_power;
struct ath12k_pdev_rssi_offsets rssi_info;
+
+ struct ath12k_thermal thermal;
};
struct ath12k_hw {
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -14057,6 +14057,10 @@ static void ath12k_mac_setup(struct ath1
init_completion(&ar->mlo_setup_done);
init_completion(&ar->completed_11d_scan);
init_completion(&ar->regd_update_completed);
+ init_completion(&ar->thermal.wmi_sync);
+
+ ar->thermal.temperature = 0;
+ ar->thermal.hwmon_dev = NULL;
INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work);
--- a/drivers/net/wireless/ath/ath12k/Makefile
+++ b/drivers/net/wireless/ath/ath12k/Makefile
@@ -30,6 +30,7 @@ ath12k-$(CPTCFG_ATH12K_TRACING) += trace
ath12k-$(CONFIG_PM) += wow.o
ath12k-$(CPTCFG_ATH12K_COREDUMP) += coredump.o
ath12k-$(CPTCFG_NL80211_TESTMODE) += testmode.o
+ath12k-$(CONFIG_THERMAL) += thermal.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include "core.h"
+#include "debug.h"
+
+static ssize_t ath12k_thermal_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ath12k *ar = dev_get_drvdata(dev);
+ unsigned long time_left;
+ int ret, temperature;
+
+ guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (ar->ah->state != ATH12K_HW_STATE_ON)
+ return -ENETDOWN;
+
+ reinit_completion(&ar->thermal.wmi_sync);
+ ret = ath12k_wmi_send_pdev_temperature_cmd(ar);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to read temperature %d\n", ret);
+ return ret;
+ }
+
+ if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
+ return -ESHUTDOWN;
+
+ time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
+ ATH12K_THERMAL_SYNC_TIMEOUT_HZ);
+ if (!time_left) {
+ ath12k_warn(ar->ab, "failed to synchronize thermal read\n");
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ temperature = ar->thermal.temperature;
+ spin_unlock_bh(&ar->data_lock);
+
+ /* display in millidegree celsius */
+ return sysfs_emit(buf, "%d\n", temperature * 1000);
+}
+
+void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature)
+{
+ spin_lock_bh(&ar->data_lock);
+ ar->thermal.temperature = temperature;
+ spin_unlock_bh(&ar->data_lock);
+ complete_all(&ar->thermal.wmi_sync);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, ath12k_thermal_temp, 0);
+
+static struct attribute *ath12k_hwmon_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ath12k_hwmon);
+
+int ath12k_thermal_register(struct ath12k_base *ab)
+{
+ struct ath12k *ar;
+ int i, j, ret;
+
+ if (!IS_REACHABLE(CONFIG_HWMON))
+ return 0;
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ continue;
+
+ ar->thermal.hwmon_dev =
+ hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
+ "ath12k_hwmon", ar,
+ ath12k_hwmon_groups);
+ if (IS_ERR(ar->thermal.hwmon_dev)) {
+ ret = PTR_ERR(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
+ ret);
+ for (j = i - 1; j >= 0; j--) {
+ ar = ab->pdevs[j].ar;
+ if (!ar)
+ continue;
+
+ hwmon_device_unregister(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ }
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void ath12k_thermal_unregister(struct ath12k_base *ab)
+{
+ struct ath12k *ar;
+ int i;
+
+ if (!IS_REACHABLE(CONFIG_HWMON))
+ return;
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ continue;
+
+ if (ar->thermal.hwmon_dev) {
+ hwmon_device_unregister(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ }
+ }
+}
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef _ATH12K_THERMAL_
+#define _ATH12K_THERMAL_
+
+#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
+
+struct ath12k_thermal {
+ struct completion wmi_sync;
+
+ /* temperature value in Celsius degree protected by data_lock. */
+ int temperature;
+ struct device *hwmon_dev;
+};
+
+#if IS_REACHABLE(CONFIG_THERMAL)
+int ath12k_thermal_register(struct ath12k_base *ab);
+void ath12k_thermal_unregister(struct ath12k_base *ab);
+void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
+#else
+static inline int ath12k_thermal_register(struct ath12k_base *ab)
+{
+ return 0;
+}
+
+static inline void ath12k_thermal_unregister(struct ath12k_base *ab)
+{
+}
+
+static inline void ath12k_thermal_event_temperature(struct ath12k *ar,
+ int temperature)
+{
+}
+
+#endif
+#endif /* _ATH12K_THERMAL_ */
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -6674,31 +6674,6 @@ static int ath12k_pull_peer_assoc_conf_e
return 0;
}
-static int
-ath12k_pull_pdev_temp_ev(struct ath12k_base *ab, struct sk_buff *skb,
- const struct wmi_pdev_temperature_event *ev)
-{
- const void **tb;
- int ret;
-
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
- if (IS_ERR(tb)) {
- ret = PTR_ERR(tb);
- ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
- return ret;
- }
-
- ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
- if (!ev) {
- ath12k_warn(ab, "failed to fetch pdev temp ev");
- kfree(tb);
- return -EPROTO;
- }
-
- kfree(tb);
- return 0;
-}
-
static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab)
{
/* try to send pending beacons first. they take priority */
@@ -8713,25 +8688,45 @@ static void
ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
+ const struct wmi_pdev_temperature_event *ev;
struct ath12k *ar;
- struct wmi_pdev_temperature_event ev = {};
+ const void **tb;
+ int temp;
+ u32 pdev_id;
- if (ath12k_pull_pdev_temp_ev(ab, skb, &ev) != 0) {
- ath12k_warn(ab, "failed to extract pdev temperature event");
+ tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
return;
}
+ ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch pdev temp ev\n");
+ kfree(tb);
+ return;
+ }
+
+ temp = a_sle32_to_cpu(ev->temp);
+ pdev_id = le32_to_cpu(ev->pdev_id);
+
+ kfree(tb);
+
ath12k_dbg(ab, ATH12K_DBG_WMI,
- "pdev temperature ev temp %d pdev_id %d\n", ev.temp, ev.pdev_id);
+ "pdev temperature ev temp %d pdev_id %u\n",
+ temp, pdev_id);
rcu_read_lock();
- ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev.pdev_id));
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id);
if (!ar) {
- ath12k_warn(ab, "invalid pdev id in pdev temperature ev %d", ev.pdev_id);
+ ath12k_warn(ab, "invalid pdev id %u in pdev temperature ev\n",
+ pdev_id);
goto exit;
}
+ ath12k_thermal_event_temperature(ar, temp);
+
exit:
rcu_read_unlock();
}

View file

@ -0,0 +1,62 @@
From b6a3bb1561e98980b24f08bffe91327b29091c82 Mon Sep 17 00:00:00 2001
From: Matt Merhar <mattmerhar@protonmail.com>
Date: Wed, 4 Mar 2026 22:07:15 -0500
Subject: [PATCH] ath12k: control thermal support via symbol
As is already done for ath10k and ath11k, add a new ATH12K_THERMAL
symbol to decouple the enablement of thermal support from
CONFIG_THERMAL.
Signed-off-by: Matt Merhar <mattmerhar@protonmail.com>
---
drivers/net/wireless/ath/ath12k/Kconfig | 7 +++++++
drivers/net/wireless/ath/ath12k/Makefile | 2 +-
drivers/net/wireless/ath/ath12k/thermal.h | 2 +-
local-symbols | 1 +
4 files changed, 10 insertions(+), 2 deletions(-)
--- a/drivers/net/wireless/ath/ath12k/Kconfig
+++ b/drivers/net/wireless/ath/ath12k/Kconfig
@@ -61,3 +61,10 @@ config ATH12K_COREDUMP
If unsure, say Y to make it easier to debug problems. But if
dump collection not required choose N.
+
+config ATH12K_THERMAL
+ bool "ath12k thermal sensor support"
+ depends on ATH12K
+ depends on THERMAL
+ help
+ Enable ath12k thermal sensor support.
--- a/drivers/net/wireless/ath/ath12k/Makefile
+++ b/drivers/net/wireless/ath/ath12k/Makefile
@@ -30,7 +30,7 @@ ath12k-$(CPTCFG_ATH12K_TRACING) += trace
ath12k-$(CONFIG_PM) += wow.o
ath12k-$(CPTCFG_ATH12K_COREDUMP) += coredump.o
ath12k-$(CPTCFG_NL80211_TESTMODE) += testmode.o
-ath12k-$(CONFIG_THERMAL) += thermal.o
+ath12k-$(CPTCFG_ATH12K_THERMAL) += thermal.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
--- a/drivers/net/wireless/ath/ath12k/thermal.h
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -17,7 +17,7 @@ struct ath12k_thermal {
struct device *hwmon_dev;
};
-#if IS_REACHABLE(CONFIG_THERMAL)
+#if IS_REACHABLE(CPTCFG_ATH12K_THERMAL)
int ath12k_thermal_register(struct ath12k_base *ab);
void ath12k_thermal_unregister(struct ath12k_base *ab);
void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
--- a/local-symbols
+++ b/local-symbols
@@ -170,6 +170,7 @@ ATH12K_DEBUG=
ATH12K_DEBUGFS=
ATH12K_TRACING=
ATH12K_COREDUMP=
+ATH12K_THERMAL=
WLAN_VENDOR_ATMEL=
AT76C50X_USB=
WLAN_VENDOR_BROADCOM=

View file

@ -21,7 +21,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/local-symbols
+++ b/local-symbols
@@ -336,6 +336,7 @@ RT2X00_LIB_FIRMWARE=
@@ -337,6 +337,7 @@ RT2X00_LIB_FIRMWARE=
RT2X00_LIB_CRYPTO=
RT2X00_LIB_LEDS=
RT2X00_LIB_DEBUGFS=