* [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support
@ 2026-03-31 14:24 Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event Maharaja Kennadyrajan
` (6 more replies)
0 siblings, 7 replies; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-03-31 14:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
Patch 1 handles the firmware stats event so we can track the current
temperature and throttle level per pdev without spamming logs.
Patch 2 enables thermal throttling at bring-up and programs default level
tables to firmware via WMI_THERM_THROT_SET_CONF_CMDID; the driver picks
IPA/XFEM defaults based on the firmware WMI service bitmap, supports 4 or 5
levels as advertised, and only fills optional fields (pout reduction,
tx chain mask) when the corresponding WMI service bits are present.
Patch 3 refactors per-radio thermal hwmon cleanup to reduce code duplication and
ensure consistent cleanup across thermal register and unregister paths.
Patch 4 reorders the group teardown logic symmetric for safe thermal sysfs cleanup.
Patch 5 exposes a thermal cooling device per radio so the kernel thermal
framework or userspace can set the TX duty-cycle off percentage; writes
are validated against the throttling state range and host state is kept in
sync with successful firmware updates.
Examples:
echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_stat
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_state
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/max_state
Maharaja Kennadyrajan (5):
wifi: ath12k: handle thermal throttle stats WMI event
wifi: ath12k: configure firmware thermal throttling via WMI
wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
wifi: ath12k: add thermal cooling device support
drivers/net/wireless/ath/ath12k/core.c | 50 +++--
drivers/net/wireless/ath/ath12k/mac.c | 9 +
drivers/net/wireless/ath/ath12k/thermal.c | 252 ++++++++++++++++++----
drivers/net/wireless/ath/ath12k/thermal.h | 35 +++
drivers/net/wireless/ath/ath12k/wmi.c | 107 +++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 50 +++++
6 files changed, 446 insertions(+), 57 deletions(-)
base-commit: dbd94b9831bc52a1efb7ff3de841ffc3457428ce
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
@ 2026-03-31 14:24 ` Maharaja Kennadyrajan
2026-04-10 17:24 ` Jeff Johnson
2026-03-31 14:24 ` [PATCH ath-next 2/5] wifi: ath12k: configure firmware thermal throttling via WMI Maharaja Kennadyrajan
` (5 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-03-31 14:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
Add handling for WMI_THERM_THROT_STATS_EVENTID by defining the
wmi_therm_throt_stats_event TLV and parsing pdev_id, temperature and
throttle level.
The firmware can emit this event periodically, including when the
throttle level is 0.
Log the received thermal throttle stats to get the current temperature level,
temperature and thermal throttling levels.
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
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/wmi.c | 38 +++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 8 ++++++
2 files changed, 46 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 65a05a9520ff..34184d0d03ff 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -8819,6 +8819,41 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
rcu_read_unlock();
}
+static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ const struct wmi_therm_throt_stats_event *ev;
+ struct ath12k *ar;
+
+ const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath12k_err(ab, "failed to parse thermal throttling stats tlv: %ld\n",
+ PTR_ERR(tb));
+ return;
+ }
+
+ ev = tb[WMI_TAG_THERM_THROT_STATS_EVENT];
+ if (!ev) {
+ ath12k_err(ab, "failed to fetch thermal throt stats ev\n");
+ return;
+ }
+
+ rcu_read_lock();
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev->pdev_id));
+ if (!ar) {
+ ath12k_warn(ab, "received thermal_throt_stats in invalid pdev %u\n",
+ le32_to_cpu(ev->pdev_id));
+ rcu_read_unlock();
+ return;
+ }
+ rcu_read_unlock();
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "thermal stats ev level %u pdev_id %u temp %u throt_levels %u\n",
+ le32_to_cpu(ev->level), le32_to_cpu(ev->pdev_id),
+ le32_to_cpu(ev->temp), le32_to_cpu(ev->therm_throt_levels));
+}
+
static void ath12k_fils_discovery_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
@@ -9900,6 +9935,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_PDEV_TEMPERATURE_EVENTID:
ath12k_wmi_pdev_temperature_event(ab, skb);
break;
+ case WMI_THERM_THROT_STATS_EVENTID:
+ ath12k_wmi_thermal_throt_stats_event(ab, skb);
+ break;
case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID:
ath12k_wmi_pdev_dma_ring_buf_release_event(ab, skb);
break;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 5ba9b7d3a888..8539435c292d 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -870,6 +870,7 @@ enum wmi_tlv_event_id {
WMI_READ_DATA_FROM_FLASH_EVENTID,
WMI_REPORT_RX_AGGR_FAILURE_EVENTID,
WMI_PKGID_EVENTID,
+ WMI_THERM_THROT_STATS_EVENTID,
WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO),
WMI_UPLOADH_EVENTID,
WMI_CAPTUREH_EVENTID,
@@ -4120,6 +4121,13 @@ enum set_init_cc_flags {
ALPHA_IS_SET,
};
+struct wmi_therm_throt_stats_event {
+ __le32 pdev_id;
+ __le32 temp;
+ __le32 level;
+ __le32 therm_throt_levels;
+} __packed;
+
struct ath12k_wmi_init_country_arg {
union {
u16 country_code;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ath-next 2/5] wifi: ath12k: configure firmware thermal throttling via WMI
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event Maharaja Kennadyrajan
@ 2026-03-31 14:24 ` Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 3/5] wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup Maharaja Kennadyrajan
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-03-31 14:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
Ath12k firmware supports thermal-throttling but requires the host to explicitly
program throttle levels and mitigation actions via WMI. Without this configuration,
firmware-driven thermal mitigation remains inactive or relies on platform-specific
defaults.
Add host-side support to build and send thermal-throttle configuration using
WMI_THERM_THROT_SET_CONF_CMDID during MAC radio start, ensuring thermal parameters
are programmed before data traffic begins.
Maintain per-radio storage for thermal throttle levels and provide conservative
default level tables for Internal Power Amplifier Device (IPA) and External Power
Amplifier Device or External Front End Module (XFEM) targets. The appropriate
default table is selected based on firmware-advertised service bits, allowing the
host to align with target thermal mitigation capabilities. If the WMI TLV service
WMI_TLV_SERVICE_IS_TARGET_IPA bit is set, then host selects the thermal throttle
values from IPA index from the table and selects values from XFEM index from the
table if this WMI TLV service bit is not set.
Build and send the thermal throttle configuration request with either 4 or
5 levels depending on firmware capability, and populate optional fields
(pout reduction and tx chain mask) only when the corresponding service bits
are advertised.
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
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 8 +++
drivers/net/wireless/ath/ath12k/thermal.c | 64 +++++++++++++++++++++
drivers/net/wireless/ath/ath12k/thermal.h | 21 +++++++
drivers/net/wireless/ath/ath12k/wmi.c | 69 +++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 42 ++++++++++++++
5 files changed, 204 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 553ec28b6aaa..21430a70aa7c 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -9673,6 +9673,12 @@ static int ath12k_mac_start(struct ath12k *ar)
}
}
+ ret = ath12k_thermal_throttling_config_default(ar);
+ if (ret) {
+ ath12k_err(ab, "failed to set thermal throttle: %d\n", ret);
+ goto err;
+ }
+
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx],
&ab->pdevs[ar->pdev_idx]);
@@ -14461,6 +14467,8 @@ static int ath12k_mac_setup_register(struct ath12k *ar,
ar->rssi_info.temp_offset = 0;
ar->rssi_info.noise_floor = ar->rssi_info.min_nf_dbm + ar->rssi_info.temp_offset;
+ ath12k_thermal_init_configs(ar);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
index a764d2112a3c..4f76622e8117 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.c
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -12,6 +12,70 @@
#include "core.h"
#include "debug.h"
+static const struct ath12k_wmi_tt_level_config_param
+tt_level_configs[ATH12K_TT_CFG_IDX_MAX][ENHANCED_THERMAL_LEVELS] = {
+ [ATH12K_TT_CFG_IDX_IPA] = {
+ [0] = { .tmplwm = -100, .tmphwm = 115, .dcoffpercent = 0,
+ .pout_reduction_db = 0 },
+ [1] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 0,
+ .pout_reduction_db = 12 },
+ [2] = { .tmplwm = 115, .tmphwm = 125, .dcoffpercent = 50,
+ .pout_reduction_db = 12 },
+ [3] = { .tmplwm = 120, .tmphwm = 130, .dcoffpercent = 90,
+ .pout_reduction_db = 12 },
+ [4] = { .tmplwm = 125, .tmphwm = 130, .dcoffpercent = 100,
+ .pout_reduction_db = 12 },
+ },
+ [ATH12K_TT_CFG_IDX_XFEM] = {
+ [0] = { .tmplwm = -100, .tmphwm = 105, .dcoffpercent = 0,
+ .pout_reduction_db = 0 },
+ [1] = { .tmplwm = 100, .tmphwm = 110, .dcoffpercent = 0,
+ .pout_reduction_db = 0 },
+ [2] = { .tmplwm = 105, .tmphwm = 115, .dcoffpercent = 50,
+ .pout_reduction_db = 0 },
+ [3] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 90,
+ .pout_reduction_db = 0 },
+ [4] = { .tmplwm = 115, .tmphwm = 120, .dcoffpercent = 100,
+ .pout_reduction_db = 0 },
+ },
+};
+
+static enum ath12k_thermal_cfg_idx ath12k_thermal_cfg_index(struct ath12k *ar)
+{
+ if (test_bit(WMI_TLV_SERVICE_IS_TARGET_IPA, ar->ab->wmi_ab.svc_map))
+ return ATH12K_TT_CFG_IDX_IPA;
+
+ return ATH12K_TT_CFG_IDX_XFEM;
+}
+
+int ath12k_thermal_throttling_config_default(struct ath12k *ar)
+{
+ struct ath12k_wmi_thermal_mitigation_arg param = {};
+ int ret;
+
+ if (test_bit(WMI_TLV_SERVICE_THERM_THROT_5_LEVELS, ar->ab->wmi_ab.svc_map))
+ param.num_levels = ENHANCED_THERMAL_LEVELS;
+ else
+ param.num_levels = THERMAL_LEVELS;
+
+ param.levelconf = ar->thermal.tt_level_configs;
+
+ ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed to send thermal mitigation cmd for default config: %d\n",
+ ret);
+ return ret;
+}
+
+void ath12k_thermal_init_configs(struct ath12k *ar)
+{
+ enum ath12k_thermal_cfg_idx cfg_idx;
+
+ cfg_idx = ath12k_thermal_cfg_index(ar);
+ ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0];
+}
+
static ssize_t ath12k_thermal_temp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h
index 9d84056188e1..33231bb3683c 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.h
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -9,18 +9,31 @@
#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
+#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100
+
+enum ath12k_thermal_cfg_idx {
+ /* Internal Power Amplifier Device */
+ ATH12K_TT_CFG_IDX_IPA,
+ /* External Power Amplifier Device or External Front End Module */
+ ATH12K_TT_CFG_IDX_XFEM,
+ ATH12K_TT_CFG_IDX_MAX,
+};
+
struct ath12k_thermal {
struct completion wmi_sync;
/* temperature value in Celsius degree protected by data_lock. */
int temperature;
struct device *hwmon_dev;
+ const struct ath12k_wmi_tt_level_config_param *tt_level_configs;
};
#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);
+int ath12k_thermal_throttling_config_default(struct ath12k *ar);
+void ath12k_thermal_init_configs(struct ath12k *ar);
#else
static inline int ath12k_thermal_register(struct ath12k_base *ab)
{
@@ -36,5 +49,13 @@ static inline void ath12k_thermal_event_temperature(struct ath12k *ar,
{
}
+static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar)
+{
+ return 0;
+}
+
+static inline void ath12k_thermal_init_configs(struct ath12k *ar)
+{
+}
#endif
#endif /* _ATH12K_THERMAL_ */
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 34184d0d03ff..b239b436b745 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -3384,6 +3384,75 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar,
return ret;
}
+int
+ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar,
+ struct ath12k_wmi_thermal_mitigation_arg *arg)
+{
+ struct ath12k_wmi_therm_throt_level_config_param *lvl_conf;
+ struct ath12k_wmi_therm_throt_config_request_cmd *cmd;
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_tlv *tlv;
+ struct sk_buff *skb;
+ int i, ret, len;
+
+ len = sizeof(*cmd) + TLV_HDR_SIZE + (arg->num_levels * sizeof(*lvl_conf));
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct ath12k_wmi_therm_throt_config_request_cmd *)skb->data;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_CONFIG_REQUEST,
+ sizeof(*cmd));
+ cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
+ cmd->enable = cpu_to_le32(1);
+ cmd->dc = cpu_to_le32(100);
+ cmd->dc_per_event = cpu_to_le32(0xffffffff);
+ cmd->therm_throt_levels = cpu_to_le32(arg->num_levels);
+
+ tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT,
+ arg->num_levels * sizeof(*lvl_conf));
+
+ lvl_conf = (struct ath12k_wmi_therm_throt_level_config_param *)tlv->value;
+
+ for (i = 0; i < arg->num_levels; i++) {
+ lvl_conf->tlv_header =
+ ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO,
+ sizeof(*lvl_conf));
+
+ lvl_conf->temp_lwm = a_cpu_to_sle32(arg->levelconf[i].tmplwm);
+ lvl_conf->temp_hwm = a_cpu_to_sle32(arg->levelconf[i].tmphwm);
+ lvl_conf->dc_off_percent = cpu_to_le32(arg->levelconf[i].dcoffpercent);
+
+ if (test_bit(WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION,
+ ar->ab->wmi_ab.svc_map))
+ lvl_conf->pout_reduction_25db =
+ cpu_to_le32(arg->levelconf[i].pout_reduction_db);
+
+ if (test_bit(WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK,
+ ar->ab->wmi_ab.svc_map))
+ lvl_conf->tx_chain_mask = cpu_to_le32(ar->cfg_tx_chainmask);
+
+ lvl_conf->duty_cycle = cpu_to_le32(ATH12K_THERMAL_DEFAULT_DUTY_CYCLE);
+ lvl_conf++;
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "WMI vdev set thermal throt pdev_id %u enable dc 100 dc_per_event 0xffffffff levels %d\n",
+ ar->pdev->pdev_id, arg->num_levels);
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to send WMI_THERM_THROT_SET_CONF cmd: %d\n",
+ ret);
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar,
struct wmi_11d_scan_start_arg *arg)
{
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 8539435c292d..59b2b42161e1 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -2274,6 +2274,10 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361,
WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365,
+ WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410,
+ WMI_TLV_SERVICE_IS_TARGET_IPA = 425,
+ WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426,
+ WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429,
WMI_TLV_SERVICE_ETH_OFFLOAD = 461,
WMI_MAX_EXT2_SERVICE,
@@ -4128,6 +4132,42 @@ struct wmi_therm_throt_stats_event {
__le32 therm_throt_levels;
} __packed;
+#define THERMAL_LEVELS 4
+#define ENHANCED_THERMAL_LEVELS 5
+
+struct ath12k_wmi_tt_level_config_param {
+ s32 tmplwm;
+ s32 tmphwm;
+ u32 dcoffpercent;
+ u32 pout_reduction_db;
+};
+
+struct ath12k_wmi_therm_throt_config_request_cmd {
+ __le32 tlv_header;
+ __le32 pdev_id;
+ __le32 enable;
+ __le32 dc;
+ /* After how many duty cycles the firmware sends stats to host */
+ __le32 dc_per_event;
+ __le32 therm_throt_levels;
+} __packed;
+
+struct ath12k_wmi_therm_throt_level_config_param {
+ __le32 tlv_header;
+ a_sle32 temp_lwm;
+ a_sle32 temp_hwm;
+ __le32 dc_off_percent;
+ __le32 prio;
+ __le32 pout_reduction_25db;
+ __le32 tx_chain_mask;
+ __le32 duty_cycle;
+} __packed;
+
+struct ath12k_wmi_thermal_mitigation_arg {
+ int num_levels;
+ const struct ath12k_wmi_tt_level_config_param *levelconf;
+};
+
struct ath12k_wmi_init_country_arg {
union {
u16 country_code;
@@ -6522,6 +6562,8 @@ __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len);
int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
enum wmi_halphy_ctrl_path_stats_id tpc_stats_type);
void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar);
+int ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar,
+ struct ath12k_wmi_thermal_mitigation_arg *arg);
static inline u32
ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param)
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ath-next 3/5] wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 2/5] wifi: ath12k: configure firmware thermal throttling via WMI Maharaja Kennadyrajan
@ 2026-03-31 14:24 ` Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 4/5] wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup Maharaja Kennadyrajan
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-03-31 14:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
Both the error path in thermal registration and the normal thermal unregister
path performed the same hwmon device unregistration and pointer cleanup.
Consolidate this logic into a single helper to reduce code duplication and ensure
consistent cleanup across all paths. Add a helper to set up the hwmon registration
during thermal registration to keep symmetry with thermal cleanup.
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
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/thermal.c | 83 +++++++++++++----------
1 file changed, 47 insertions(+), 36 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
index 4f76622e8117..6f70c11c1098 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.c
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -130,59 +130,70 @@ static struct attribute *ath12k_hwmon_attrs[] = {
};
ATTRIBUTE_GROUPS(ath12k_hwmon);
-int ath12k_thermal_register(struct ath12k_base *ab)
+static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
+{
+ struct ath12k *ar;
+ int ret;
+
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ return 0;
+
+ 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);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
{
struct ath12k *ar;
- int i, j, ret;
+
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ return;
+
+ hwmon_device_unregister(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+}
+
+int ath12k_thermal_register(struct ath12k_base *ab)
+{
+ int i, 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;
- }
+ ret = ath12k_thermal_setup_radio(ab, i);
+ if (ret)
+ goto out;
}
return 0;
+out:
+ for (i--; i >= 0; i--)
+ ath12k_thermal_cleanup_radio(ab, i);
+
+ return ret;
}
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;
- }
- }
+ for (i = 0; i < ab->num_radios; i++)
+ ath12k_thermal_cleanup_radio(ab, i);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ath-next 4/5] wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
` (2 preceding siblings ...)
2026-03-31 14:24 ` [PATCH ath-next 3/5] wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup Maharaja Kennadyrajan
@ 2026-03-31 14:24 ` Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 5/5] wifi: ath12k: add thermal cooling device support Maharaja Kennadyrajan
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-03-31 14:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
A later change adds thermal cooling device sysfs under the wiphy device kobject.
With the current teardown order, MAC/wiphy are unregistered before per-device
cleanup, so any subsequent thermal sysfs removal would run after the wiphy kobject
is gone. That ordering is asymmetric with setup and would risk kernfs issues during
removal.
This change also adjusts the position of ath12k_mac_mlo_teardown(). Previously
it ran before per-device cleanup/MAC unregister. MLO teardown issues WMI to teardown
multi-link state and is part of the MAC teardown sequence. Placing it alongside
MAC unregister (after per-device cleanup) preserves setup/teardown symmetry and
avoids racing with remaining netdev/wiphy state.
Reorder hw_group_stop() so per-device cleanup (including thermal/hwmon sysfs
removal) runs while the wiphy still exists. After per-device cleanup completes,
unregister the MAC (dropping wiphys), run ath12k_mac_mlo_teardown(), and finally
destroy the MAC. This mirrors the setup sequence and keeps sysfs cleanup safe
when introduced in a later patch.
To keep start/stop symmetry, add ath12k_core_device_setup() to encapsulate the
per-device bring-up steps (pdev create, IRQ enable, rfkill config) that were
previously open-coded in hw_group_start(). Use this helper in hw_group_start()
to match the existing per-device cleanup helper used by hw_group_stop().
Note that set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags) is now executed outside
the ab->core_lock critical section. The core_lock has not provided protection for
the REGISTERED flag, readers do not rely on core_lock for this bit, and the flag
is only toggled in the serialized group start/stop path using atomic bitops.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.c | 50 +++++++++++++++-----------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index c31c47fb5a73..d21db889794a 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1006,6 +1006,27 @@ static void ath12k_core_device_cleanup(struct ath12k_base *ab)
mutex_unlock(&ab->core_lock);
}
+static int ath12k_core_device_setup(struct ath12k_base *ab)
+{
+ int ret;
+
+ guard(mutex)(&ab->core_lock);
+
+ ret = ath12k_core_pdev_create(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to create pdev core %d\n", ret);
+ return ret;
+ }
+
+ ath12k_hif_irq_enable(ab);
+
+ ret = ath12k_core_rfkill_config(ab);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
+ return 0;
+}
+
static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
{
struct ath12k_base *ab;
@@ -1015,10 +1036,6 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
clear_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags);
- ath12k_mac_unregister(ag);
-
- ath12k_mac_mlo_teardown(ag);
-
for (i = ag->num_devices - 1; i >= 0; i--) {
ab = ag->ab[i];
if (!ab)
@@ -1029,6 +1046,12 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
ath12k_core_device_cleanup(ab);
}
+ /* Unregister MAC (drops wiphys) only after per-device cleanup */
+ ath12k_mac_unregister(ag);
+
+ /* Teardown MLO state after MAC unregister for symmetry */
+ ath12k_mac_mlo_teardown(ag);
+
ath12k_mac_destroy(ag);
}
@@ -1165,26 +1188,11 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag)
if (!ab)
continue;
- mutex_lock(&ab->core_lock);
-
set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
- ret = ath12k_core_pdev_create(ab);
- if (ret) {
- ath12k_err(ab, "failed to create pdev core %d\n", ret);
- mutex_unlock(&ab->core_lock);
- goto err;
- }
-
- ath12k_hif_irq_enable(ab);
-
- ret = ath12k_core_rfkill_config(ab);
- if (ret && ret != -EOPNOTSUPP) {
- mutex_unlock(&ab->core_lock);
+ ret = ath12k_core_device_setup(ab);
+ if (ret)
goto err;
- }
-
- mutex_unlock(&ab->core_lock);
}
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ath-next 5/5] wifi: ath12k: add thermal cooling device support
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
` (3 preceding siblings ...)
2026-03-31 14:24 ` [PATCH ath-next 4/5] wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup Maharaja Kennadyrajan
@ 2026-03-31 14:24 ` Maharaja Kennadyrajan
2026-04-09 10:34 ` [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and " Rameshkumar Sundaram
2026-04-10 2:37 ` Baochen Qiang
6 siblings, 0 replies; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-03-31 14:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
Add thermal cooling device support to control the temperature by throttling data
transmission. Throttling is performed by suspending data TX queues according to
a configured duty-cycle off percentage. The thermal cooling device allows users
to configure the duty-cycle off percentage and operate the device with the
selected value.
User configuration updates a single duty-cycle off percentage, which is applied
uniformly by the host and treated as only one temperature level. This value
remains in effect until updated again by the user. All other thermal throttling
parameters continue to use their default firmware provided values.
Reject invalid duty-cycle off percentage values that fall outside the supported
range. Register a cooling device to allow the thermal framework to query and set
the current throttle state, report the maximum supported state, and keep the
host state in sync with successful firmware updates. A throttle state of zero
restores the default firmware thermal configuration.
Command to set the duty-cycle off percent:
echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/cur_state
Command to read duty-cycle off percent:
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/cur_state
Command to read the maximum duty-cycle off percent:
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/max_state
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
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 1 +
drivers/net/wireless/ath/ath12k/thermal.c | 107 +++++++++++++++++++++-
drivers/net/wireless/ath/ath12k/thermal.h | 14 +++
3 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 21430a70aa7c..40395c9a8bea 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -14821,6 +14821,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
init_completion(&ar->completed_11d_scan);
init_completion(&ar->regd_update_completed);
init_completion(&ar->thermal.wmi_sync);
+ mutex_init(&ar->thermal.lock);
ar->thermal.temperature = 0;
ar->thermal.hwmon_dev = NULL;
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
index 6f70c11c1098..97fc49c40ac1 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.c
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -76,6 +76,73 @@ void ath12k_thermal_init_configs(struct ath12k *ar)
ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0];
}
+static int
+ath12k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = ATH12K_THERMAL_THROTTLE_MAX;
+
+ return 0;
+}
+
+static int
+ath12k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct ath12k *ar = cdev->devdata;
+
+ mutex_lock(&ar->thermal.lock);
+ *state = ar->thermal.throttle_state;
+ mutex_unlock(&ar->thermal.lock);
+
+ return 0;
+}
+
+int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state)
+{
+ struct ath12k_wmi_thermal_mitigation_arg param = {};
+ struct ath12k_wmi_tt_level_config_param cfg = {};
+ int ret;
+
+ param.num_levels = 1;
+ cfg.dcoffpercent = throttle_state;
+ param.levelconf = &cfg;
+
+ ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to send thermal mitigation cmd: %d\n",
+ ret);
+
+ return ret;
+}
+
+static int
+ath12k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long throttle_state)
+{
+ struct ath12k *ar = cdev->devdata;
+
+ if (throttle_state > ATH12K_THERMAL_THROTTLE_MAX)
+ return -EINVAL;
+
+ scoped_guard(mutex, &ar->thermal.lock) {
+ if (ar->thermal.throttle_state == throttle_state)
+ return 0;
+ ar->thermal.throttle_state = throttle_state;
+ }
+
+ if (throttle_state == 0)
+ return ath12k_thermal_throttling_config_default(ar);
+
+ return ath12k_thermal_set_throttling(ar, throttle_state);
+}
+
+static const struct thermal_cooling_device_ops ath12k_thermal_ops = {
+ .get_max_state = ath12k_thermal_get_max_throttle_state,
+ .get_cur_state = ath12k_thermal_get_cur_throttle_state,
+ .set_cur_state = ath12k_thermal_set_cur_throttle_state,
+};
+
static ssize_t ath12k_thermal_temp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -132,6 +199,7 @@ ATTRIBUTE_GROUPS(ath12k_hwmon);
static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
{
+ char pdev_name[20];
struct ath12k *ar;
int ret;
@@ -139,6 +207,28 @@ static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
if (!ar)
return 0;
+ ar->thermal.cdev =
+ thermal_cooling_device_register("ath12k_thermal", ar,
+ &ath12k_thermal_ops);
+ if (IS_ERR(ar->thermal.cdev)) {
+ ret = PTR_ERR(ar->thermal.cdev);
+ ar->thermal.cdev = NULL;
+ ath12k_err(ar->ab, "failed to register cooling device: %d\n",
+ ret);
+ return ret;
+ }
+
+ scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u",
+ ar->hw_link_id);
+
+ ret = sysfs_create_link(&ar->ah->hw->wiphy->dev.kobj,
+ &ar->thermal.cdev->device.kobj, pdev_name);
+ if (ret) {
+ ath12k_err(ab, "failed to create cooling device symlink: %d\n",
+ ret);
+ goto unregister_cdev;
+ }
+
ar->thermal.hwmon_dev =
hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
"ath12k_hwmon", ar,
@@ -148,14 +238,22 @@ static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
ar->thermal.hwmon_dev = NULL;
ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
ret);
- return ret;
+ goto remove_sysfs;
}
return 0;
+
+remove_sysfs:
+ sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name);
+unregister_cdev:
+ thermal_cooling_device_unregister(ar->thermal.cdev);
+ ar->thermal.cdev = NULL;
+ return ret;
}
static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
{
+ char pdev_name[20];
struct ath12k *ar;
ar = ab->pdevs[i].ar;
@@ -164,6 +262,13 @@ static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
hwmon_device_unregister(ar->thermal.hwmon_dev);
ar->thermal.hwmon_dev = NULL;
+
+ scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u",
+ ar->hw_link_id);
+ sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name);
+
+ thermal_cooling_device_unregister(ar->thermal.cdev);
+ ar->thermal.cdev = NULL;
}
int ath12k_thermal_register(struct ath12k_base *ab)
diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h
index 33231bb3683c..30e7b0880e05 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.h
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -7,9 +7,12 @@
#ifndef _ATH12K_THERMAL_
#define _ATH12K_THERMAL_
+#include <linux/mutex.h>
+
#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100
+#define ATH12K_THERMAL_THROTTLE_MAX 100
enum ath12k_thermal_cfg_idx {
/* Internal Power Amplifier Device */
@@ -26,6 +29,10 @@ struct ath12k_thermal {
int temperature;
struct device *hwmon_dev;
const struct ath12k_wmi_tt_level_config_param *tt_level_configs;
+ struct thermal_cooling_device *cdev;
+ /* Serialize thermal operations and hwmon reads */
+ struct mutex lock;
+ u32 throttle_state;
};
#if IS_REACHABLE(CONFIG_THERMAL)
@@ -34,6 +41,7 @@ void ath12k_thermal_unregister(struct ath12k_base *ab);
void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
int ath12k_thermal_throttling_config_default(struct ath12k *ar);
void ath12k_thermal_init_configs(struct ath12k *ar);
+int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state);
#else
static inline int ath12k_thermal_register(struct ath12k_base *ab)
{
@@ -57,5 +65,11 @@ static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar)
static inline void ath12k_thermal_init_configs(struct ath12k *ar)
{
}
+
+static inline int ath12k_thermal_set_throttling(struct ath12k *ar,
+ u32 throttle_state)
+{
+ return 0;
+}
#endif
#endif /* _ATH12K_THERMAL_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
` (4 preceding siblings ...)
2026-03-31 14:24 ` [PATCH ath-next 5/5] wifi: ath12k: add thermal cooling device support Maharaja Kennadyrajan
@ 2026-04-09 10:34 ` Rameshkumar Sundaram
2026-04-10 2:37 ` Baochen Qiang
6 siblings, 0 replies; 10+ messages in thread
From: Rameshkumar Sundaram @ 2026-04-09 10:34 UTC (permalink / raw)
To: Maharaja Kennadyrajan, ath12k; +Cc: linux-wireless
On 3/31/2026 7:54 PM, Maharaja Kennadyrajan wrote:
> Patch 1 handles the firmware stats event so we can track the current
> temperature and throttle level per pdev without spamming logs.
>
> Patch 2 enables thermal throttling at bring-up and programs default level
> tables to firmware via WMI_THERM_THROT_SET_CONF_CMDID; the driver picks
> IPA/XFEM defaults based on the firmware WMI service bitmap, supports 4 or 5
> levels as advertised, and only fills optional fields (pout reduction,
> tx chain mask) when the corresponding WMI service bits are present.
>
> Patch 3 refactors per-radio thermal hwmon cleanup to reduce code duplication and
> ensure consistent cleanup across thermal register and unregister paths.
>
> Patch 4 reorders the group teardown logic symmetric for safe thermal sysfs cleanup.
>
> Patch 5 exposes a thermal cooling device per radio so the kernel thermal
> framework or userspace can set the TX duty-cycle off percentage; writes
> are validated against the throttling state range and host state is kept in
> sync with successful firmware updates.
>
> Examples:
> echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_stat
> cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_state
> cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/max_state
>
> Maharaja Kennadyrajan (5):
> wifi: ath12k: handle thermal throttle stats WMI event
> wifi: ath12k: configure firmware thermal throttling via WMI
> wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
> wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
> wifi: ath12k: add thermal cooling device support
>
> drivers/net/wireless/ath/ath12k/core.c | 50 +++--
> drivers/net/wireless/ath/ath12k/mac.c | 9 +
> drivers/net/wireless/ath/ath12k/thermal.c | 252 ++++++++++++++++++----
> drivers/net/wireless/ath/ath12k/thermal.h | 35 +++
> drivers/net/wireless/ath/ath12k/wmi.c | 107 +++++++++
> drivers/net/wireless/ath/ath12k/wmi.h | 50 +++++
> 6 files changed, 446 insertions(+), 57 deletions(-)
>
>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
` (5 preceding siblings ...)
2026-04-09 10:34 ` [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and " Rameshkumar Sundaram
@ 2026-04-10 2:37 ` Baochen Qiang
6 siblings, 0 replies; 10+ messages in thread
From: Baochen Qiang @ 2026-04-10 2:37 UTC (permalink / raw)
To: Maharaja Kennadyrajan, ath12k; +Cc: linux-wireless
On 3/31/2026 10:24 PM, Maharaja Kennadyrajan wrote:
> Patch 1 handles the firmware stats event so we can track the current
> temperature and throttle level per pdev without spamming logs.
>
> Patch 2 enables thermal throttling at bring-up and programs default level
> tables to firmware via WMI_THERM_THROT_SET_CONF_CMDID; the driver picks
> IPA/XFEM defaults based on the firmware WMI service bitmap, supports 4 or 5
> levels as advertised, and only fills optional fields (pout reduction,
> tx chain mask) when the corresponding WMI service bits are present.
>
> Patch 3 refactors per-radio thermal hwmon cleanup to reduce code duplication and
> ensure consistent cleanup across thermal register and unregister paths.
>
> Patch 4 reorders the group teardown logic symmetric for safe thermal sysfs cleanup.
>
> Patch 5 exposes a thermal cooling device per radio so the kernel thermal
> framework or userspace can set the TX duty-cycle off percentage; writes
> are validated against the throttling state range and host state is kept in
> sync with successful firmware updates.
>
> Examples:
> echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_stat
> cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_state
> cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/max_state
>
> Maharaja Kennadyrajan (5):
> wifi: ath12k: handle thermal throttle stats WMI event
> wifi: ath12k: configure firmware thermal throttling via WMI
> wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
> wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
> wifi: ath12k: add thermal cooling device support
>
> drivers/net/wireless/ath/ath12k/core.c | 50 +++--
> drivers/net/wireless/ath/ath12k/mac.c | 9 +
> drivers/net/wireless/ath/ath12k/thermal.c | 252 ++++++++++++++++++----
> drivers/net/wireless/ath/ath12k/thermal.h | 35 +++
> drivers/net/wireless/ath/ath12k/wmi.c | 107 +++++++++
> drivers/net/wireless/ath/ath12k/wmi.h | 50 +++++
> 6 files changed, 446 insertions(+), 57 deletions(-)
>
>
> base-commit: dbd94b9831bc52a1efb7ff3de841ffc3457428ce
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event
2026-03-31 14:24 ` [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event Maharaja Kennadyrajan
@ 2026-04-10 17:24 ` Jeff Johnson
2026-04-13 15:29 ` Maharaja Kennadyrajan
0 siblings, 1 reply; 10+ messages in thread
From: Jeff Johnson @ 2026-04-10 17:24 UTC (permalink / raw)
To: Maharaja Kennadyrajan, ath12k; +Cc: linux-wireless
On 3/31/2026 7:24 AM, Maharaja Kennadyrajan wrote:
> +static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab,
> + struct sk_buff *skb)
> +{
> + const struct wmi_therm_throt_stats_event *ev;
> + struct ath12k *ar;
> +
> + const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
please rebase on current ath-main, this interface changed with:
https://msgid.link/20260407095426.3285574-1-nico.escande@gmail.com
/jeff
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event
2026-04-10 17:24 ` Jeff Johnson
@ 2026-04-13 15:29 ` Maharaja Kennadyrajan
0 siblings, 0 replies; 10+ messages in thread
From: Maharaja Kennadyrajan @ 2026-04-13 15:29 UTC (permalink / raw)
To: Jeff Johnson, ath12k; +Cc: linux-wireless
On 10-04-2026 10:54 pm, Jeff Johnson wrote:
> On 3/31/2026 7:24 AM, Maharaja Kennadyrajan wrote:
>> +static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab,
>> + struct sk_buff *skb)
>> +{
>> + const struct wmi_therm_throt_stats_event *ev;
>> + struct ath12k *ar;
>> +
>> + const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
> please rebase on current ath-main, this interface changed with:
> https://msgid.link/20260407095426.3285574-1-nico.escande@gmail.com
sure, I will rebase on latest TOT with new interface and send the next
version.
> /jeff
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-04-13 15:30 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-31 14:24 [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and cooling device support Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event Maharaja Kennadyrajan
2026-04-10 17:24 ` Jeff Johnson
2026-04-13 15:29 ` Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 2/5] wifi: ath12k: configure firmware thermal throttling via WMI Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 3/5] wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 4/5] wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup Maharaja Kennadyrajan
2026-03-31 14:24 ` [PATCH ath-next 5/5] wifi: ath12k: add thermal cooling device support Maharaja Kennadyrajan
2026-04-09 10:34 ` [PATCH ath-next 0/5] wifi: ath12k: thermal throttling and " Rameshkumar Sundaram
2026-04-10 2:37 ` Baochen Qiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox