ath.git patches for v7.2 (PR #2)

For ath12k:
 - Add thermal throttling and cooling device support
 - Add support for handling incumbent signal interference in 6 GHz
 - Add support for channel 177 in the 5 GHz band
 
 In addition, a large number of cleanup and minor bug fixing across
 all supported drivers.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQ/mtSHzPUi16IfDEksFbugiYzLewUCahXEHAAKCRAsFbugiYzL
 e4+OAQCK3CILFdb8JbEVgROb/bfAmGWCAkdEBIMxEbGITQSQCAEAsdLEntym8eJA
 rjCjLNz62Dn5KoQLaV436nCjC+UjWwo=
 =3Pqg
 -----END PGP SIGNATURE-----

Merge tag 'ath-next-20260526' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath

Jeff Johnson says:
==================
ath.git patches for v7.2 (PR #2)

For ath12k:
- Add thermal throttling and cooling device support
- Add support for handling incumbent signal interference in 6 GHz
- Add support for channel 177 in the 5 GHz band

In addition, a large number of cleanup and minor bug fixing across
all supported drivers.
==================

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-05-28 10:23:12 +02:00
commit 6f5dc19f46
30 changed files with 1180 additions and 196 deletions

View File

@ -1269,7 +1269,6 @@ struct ath10k {
} testmode;
struct {
struct gpio_led wifi_led;
struct led_classdev cdev;
char label[48];
u32 gpio_state_pin;

View File

@ -1353,7 +1353,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm
msdu_id = res;
}
/* As msdu is freed by mac80211 (in ieee80211_tx_status()) and by
/* As msdu is freed by mac80211 (in ieee80211_tx_status_skb()) and by
* ath10k (in ath10k_htt_htc_tx_complete()) we have to increase
* reference by one to avoid a use-after-free case and a double
* free.

View File

@ -19,15 +19,13 @@ static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev,
{
struct ath10k *ar = container_of(led_cdev, struct ath10k,
leds.cdev);
struct gpio_led *led = &ar->leds.wifi_led;
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_ON)
goto out;
ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low;
ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, ar->leds.gpio_state_pin);
ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, brightness == LED_OFF);
out:
mutex_unlock(&ar->conf_mutex);
@ -63,13 +61,9 @@ int ath10k_leds_register(struct ath10k *ar)
snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s",
wiphy_name(ar->hw->wiphy));
ar->leds.wifi_led.active_low = 1;
ar->leds.wifi_led.name = ar->leds.label;
ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
ar->leds.cdev.name = ar->leds.label;
ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger;
ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
if (ret)

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/completion.h>
@ -1112,7 +1112,7 @@ int ath10k_qmi_init(struct ath10k *ar, u32 msa_size)
spin_lock_init(&qmi->event_lock);
INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work);
ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01,
ret = qmi_add_lookup(&qmi->qmi_hdl, QMI_SERVICE_ID_WLFW,
WLFW_SERVICE_VERS_V01, 0);
if (ret)
goto err_qmi_lookup;

View File

@ -7,7 +7,6 @@
#ifndef WCN3990_QMI_SVC_V01_H
#define WCN3990_QMI_SVC_V01_H
#define WLFW_SERVICE_ID_V01 0x45
#define WLFW_SERVICE_VERS_V01 0x01
#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config ATH11K
tristate "Qualcomm Technologies 802.11ax chipset support"
tristate "Qualcomm 802.11ax chipset support"
depends on MAC80211 && HAS_DMA
select ATH_COMMON
select QCOM_QMI_HELPERS

View File

@ -1040,6 +1040,7 @@ void ath11k_dp_free(struct ath11k_base *ab)
idr_destroy(&dp->tx_ring[i].txbuf_idr);
spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock);
kfree(dp->tx_ring[i].tx_status);
dp->tx_ring[i].tx_status = NULL;
}
/* Deinit any SOC level resource */

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/msi.h>
@ -282,8 +282,10 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
break;
}
spin_lock_bh(&ab->base_lock);
if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
queue_work(ab->workqueue_aux, &ab->reset_work);
spin_unlock_bh(&ab->base_lock);
break;
default:

View File

@ -1210,6 +1210,14 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev)
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
spin_lock_bh(&ab->base_lock);
set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
spin_unlock_bh(&ab->base_lock);
cancel_work_sync(&ab->reset_work);
cancel_work_sync(&ab->dump_work);
ath11k_pci_power_down(ab, false);
}

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@ -3337,7 +3336,7 @@ int ath11k_qmi_init_service(struct ath11k_base *ab)
spin_lock_init(&ab->qmi.event_lock);
INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work);
ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01,
ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW,
ATH11K_QMI_WLFW_SERVICE_VERS_V01,
ab->qmi.service_ins_id);
if (ret < 0) {

View File

@ -15,7 +15,6 @@
#define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64
#define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000
#define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128
#define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45
#define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config ATH12K
tristate "Qualcomm Technologies Wi-Fi 7 support (ath12k)"
tristate "Qualcomm Wi-Fi 7 support (ath12k)"
depends on MAC80211 && HAS_DMA && PCI
select QCOM_QMI_HELPERS
select MHI_BUS
@ -15,7 +15,7 @@ config ATH12K
If you choose to build a module, it'll be called ath12k.
config ATH12K_AHB
bool "QTI ath12k AHB support"
bool "Qualcomm ath12k AHB support"
depends on ATH12K && REMOTEPROC
select QCOM_MDT_LOADER
select QCOM_SCM
@ -33,7 +33,7 @@ config ATH12K_DEBUG
you want optimal performance choose N.
config ATH12K_DEBUGFS
bool "QTI ath12k debugfs support"
bool "Qualcomm ath12k debugfs support"
depends on ATH12K && MAC80211_DEBUGFS
help
Enable ath12k debugfs support

View File

@ -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);
ret = ath12k_core_device_setup(ab);
if (ret)
goto err;
}
ath12k_hif_irq_enable(ab);
ret = ath12k_core_rfkill_config(ab);
if (ret && ret != -EOPNOTSUPP) {
mutex_unlock(&ab->core_lock);
goto err;
}
mutex_unlock(&ab->core_lock);
}
return 0;

View File

@ -542,8 +542,8 @@ struct ath12k_sta {
#define ATH12K_MAX_5GHZ_FREQ (ATH12K_5GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW)
#define ATH12K_MIN_6GHZ_FREQ (ATH12K_6GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW)
#define ATH12K_MAX_6GHZ_FREQ (ATH12K_6GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW)
#define ATH12K_NUM_CHANS 101
#define ATH12K_MAX_5GHZ_CHAN 173
#define ATH12K_NUM_CHANS 102
#define ATH12K_MAX_5GHZ_CHAN 177
static inline bool ath12k_is_2ghz_channel_freq(u32 freq)
{
@ -763,6 +763,14 @@ struct ath12k {
struct ath12k_pdev_rssi_offsets rssi_info;
struct ath12k_thermal thermal;
/* Protected by ar->data_lock */
struct ath12k_incumbent_signal_interference {
u32 center_freq;
enum nl80211_chan_width width;
u32 chan_bw_interference_bitmap;
bool handling_in_progress;
} incumbent_signal_interference;
};
struct ath12k_hw {

View File

@ -1450,6 +1450,44 @@ static const struct file_operations fops_pdev_stats = {
.llseek = default_llseek,
};
static ssize_t
ath12k_write_simulate_incumbent_signal_interference(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath12k *ar = file->private_data;
struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
struct wiphy *wiphy = ath12k_ar_to_hw(ar)->wiphy;
u32 chan_bw_interference_bitmap;
int ret;
if (ah->state != ATH12K_HW_STATE_ON)
return -ENETDOWN;
/*
* Bitmap uses the firmware primary-based ordering documented in
* ath12k_wmi_transform_interference_bitmap() & intf_map_80.
*/
if (kstrtou32_from_user(user_buf, count, 0, &chan_bw_interference_bitmap))
return -EINVAL;
wiphy_lock(wiphy);
ret = ath12k_wmi_simulate_incumbent_signal_interference(ar, chan_bw_interference_bitmap);
if (ret)
goto exit;
ret = count;
exit:
wiphy_unlock(wiphy);
return ret;
}
static const struct file_operations fops_simulate_incumbent_signal_interference = {
.write = ath12k_write_simulate_incumbent_signal_interference,
.open = simple_open,
};
static
void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
{
@ -1515,6 +1553,14 @@ void ath12k_debugfs_register(struct ath12k *ar)
ar, &fops_tpc_stats_type);
init_completion(&ar->debug.tpc_complete);
if (ar->mac.sbands[NL80211_BAND_6GHZ].channels &&
test_bit(WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT,
ar->ab->wmi_ab.svc_map)) {
debugfs_create_file("simulate_incumbent_signal_interference", 0200,
ar->debug.debugfs_pdev, ar,
&fops_simulate_incumbent_signal_interference);
}
ath12k_debugfs_htt_stats_register(ar);
ath12k_debugfs_fw_stats_register(ar);

View File

@ -17,6 +17,11 @@
#include "dp_mon.h"
#include "debugfs_htt_stats.h"
#define ATH12K_2GHZ_MIN_CHAN_NUM 1
#define ATH12K_2GHZ_MAX_CHAN_NUM 14
#define ATH12K_5GHZ_MIN_CHAN_NUM 36
#define ATH12K_5GHZ_MAX_CHAN_NUM 177
static int ath12k_dp_rx_tid_delete_handler(struct ath12k_base *ab,
struct ath12k_dp_rx_tid_rxq *rx_tid);
@ -1095,7 +1100,8 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k_pdev_dp *dp_pdev,
static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
struct sk_buff *msdu,
enum hal_encrypt_type enctype,
struct hal_rx_desc_data *rx_info)
struct hal_rx_desc_data *rx_info,
enum ath12k_dp_rx_decap_type decap_type)
{
struct ieee80211_hdr *hdr;
struct ethhdr *eth;
@ -1103,12 +1109,24 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
u8 sa[ETH_ALEN];
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct ath12k_dp_rx_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}};
struct ath12k_dp_rx_rfc1042_hdr *llc;
eth = (struct ethhdr *)msdu->data;
ether_addr_copy(da, eth->h_dest);
ether_addr_copy(sa, eth->h_source);
rfc.snap_type = eth->h_proto;
skb_pull(msdu, sizeof(*eth));
if (decap_type == DP_RX_DECAP_TYPE_8023) {
/*
* For 802.3 frames, eth->h_proto carries a length field, not
* an EtherType. The actual EtherType is in the LLC/SNAP header
* that follows the Ethernet header.
*/
llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + sizeof(*eth));
rfc.snap_type = llc->snap_type;
skb_pull(msdu, sizeof(*eth) + sizeof(*llc));
} else {
rfc.snap_type = eth->h_proto;
skb_pull(msdu, sizeof(*eth));
}
memcpy(skb_push(msdu, sizeof(rfc)), &rfc,
sizeof(rfc));
ath12k_get_dot11_hdr_from_rx_desc(dp_pdev, msdu, rxcb, enctype, rx_info);
@ -1126,9 +1144,10 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu
bool decrypted,
struct hal_rx_desc_data *rx_info)
{
enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type;
struct ethhdr *ehdr;
switch (rx_info->decap_type) {
switch (decap_type) {
case DP_RX_DECAP_TYPE_NATIVE_WIFI:
ath12k_dp_rx_h_undecap_nwifi(dp_pdev, msdu, enctype, rx_info);
break;
@ -1142,19 +1161,33 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu
/* mac80211 allows fast path only for authorized STA */
if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) {
ATH12K_SKB_RXCB(msdu)->is_eapol = true;
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}
/* PN for mcast packets will be validated in mac80211;
* remove eth header and add 802.11 header.
*/
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted)
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) {
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}
rx_info->rx_status->flag |= RX_FLAG_8023;
break;
case DP_RX_DECAP_TYPE_8023:
/* TODO: Handle undecap for these formats */
break;
/*
* Note that ethernet decap format indicates that the decapped
* packet is either Ethernet 2 (DIX) or 802.3 (uses SNAP/LLC).
*/
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) {
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}
rx_info->rx_status->flag |= RX_FLAG_8023;
}
}
EXPORT_SYMBOL(ath12k_dp_rx_h_undecap);
@ -1289,9 +1322,11 @@ void ath12k_dp_rx_h_ppdu(struct ath12k_pdev_dp *dp_pdev,
center_freq <= ATH12K_MAX_6GHZ_FREQ) {
rx_status->band = NL80211_BAND_6GHZ;
rx_status->freq = center_freq;
} else if (channel_num >= 1 && channel_num <= 14) {
} else if (channel_num >= ATH12K_2GHZ_MIN_CHAN_NUM &&
channel_num <= ATH12K_2GHZ_MAX_CHAN_NUM) {
rx_status->band = NL80211_BAND_2GHZ;
} else if (channel_num >= 36 && channel_num <= 173) {
} else if (channel_num >= ATH12K_5GHZ_MIN_CHAN_NUM &&
channel_num <= ATH12K_5GHZ_MAX_CHAN_NUM) {
rx_status->band = NL80211_BAND_5GHZ;
}
@ -1336,9 +1371,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
struct ath12k_dp_peer *peer;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct ieee80211_rx_status *status = rx_info->rx_status;
u8 decap = rx_info->decap_type;
bool is_mcbc = rxcb->is_mcbc;
bool is_eapol = rxcb->is_eapol;
peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id);
@ -1383,15 +1416,6 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
/* TODO: trace rx packet */
/* PN for multicast packets are not validate in HW,
* so skip 802.3 rx path
* Also, fast_rx expects the STA to be authorized, hence
* eapol packets are sent in slow path.
*/
if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol &&
!(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
rx_status->flag |= RX_FLAG_8023;
ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
}
EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);

View File

@ -51,6 +51,9 @@
.max_power = 30, \
}
#define ATH12K_5_9_GHZ_MIN_FREQ 5845
#define ATH12K_5_9_GHZ_MAX_FREQ 5885
static const struct ieee80211_channel ath12k_2ghz_channels[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
@ -96,6 +99,7 @@ static const struct ieee80211_channel ath12k_5ghz_channels[] = {
CHAN5G(165, 5825, 0),
CHAN5G(169, 5845, 0),
CHAN5G(173, 5865, 0),
CHAN5G(177, 5885, 0),
};
static const struct ieee80211_channel ath12k_6ghz_channels[] = {
@ -5615,12 +5619,14 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
if (ret)
goto exit;
arg = kzalloc_obj(*arg);
arg = kzalloc_flex(*arg, chan_list, n_channels);
if (!arg) {
ret = -ENOMEM;
goto exit;
}
arg->num_chan = n_channels;
ath12k_wmi_start_scan_init(ar, arg);
arg->vdev_id = arvif->vdev_id;
arg->scan_id = ATH12K_SCAN_ID;
@ -5642,18 +5648,8 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
arg->scan_f_passive = 1;
}
if (n_channels) {
arg->num_chan = n_channels;
arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list),
GFP_KERNEL);
if (!arg->chan_list) {
ret = -ENOMEM;
goto exit;
}
for (i = 0; i < arg->num_chan; i++)
arg->chan_list[i] = chan_list[i]->center_freq;
}
for (i = 0; i < arg->num_chan; i++)
arg->chan_list[i] = chan_list[i]->center_freq;
ret = ath12k_start_scan(ar, arg);
if (ret) {
@ -5678,7 +5674,6 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
exit:
if (arg) {
kfree(arg->chan_list);
kfree(arg->extraie.ptr);
kfree(arg);
}
@ -9650,6 +9645,10 @@ static int ath12k_mac_start(struct ath12k *ar)
ar->allocated_vdev_map = 0;
ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
spin_lock_bh(&ar->data_lock);
ar->incumbent_signal_interference.handling_in_progress = false;
spin_unlock_bh(&ar->data_lock);
/* Configure monitor status ring with default rx_filter to get rx status
* such as rssi, rx_duration.
*/
@ -9677,6 +9676,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]);
@ -9857,6 +9862,10 @@ static void ath12k_mac_stop(struct ath12k *ar)
synchronize_rcu();
atomic_set(&ar->num_pending_mgmt_tx, 0);
spin_lock_bh(&ar->data_lock);
ar->incumbent_signal_interference.handling_in_progress = false;
spin_unlock_bh(&ar->data_lock);
}
void ath12k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
@ -11443,8 +11452,10 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
struct ieee80211_vif_chanctx_switch *vifs,
int n_vifs)
{
struct ath12k_incumbent_signal_interference *incumbent;
struct ath12k_wmi_vdev_up_params params = {};
struct ieee80211_bss_conf *link_conf;
struct cfg80211_chan_def *chandef;
struct ath12k_base *ab = ar->ab;
struct ath12k_link_vif *arvif;
struct ieee80211_vif *vif;
@ -11556,6 +11567,42 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
if (!ath12k_mac_monitor_stop(ar))
ath12k_mac_monitor_start(ar);
}
incumbent = &ar->incumbent_signal_interference;
spin_lock_bh(&ar->data_lock);
if (incumbent->handling_in_progress) {
chandef = &vifs[0].new_ctx->def;
if (incumbent->chan_bw_interference_bitmap &
ATH12K_WMI_DCS_SEG_PRI20) {
if (incumbent->center_freq !=
chandef->chan->center_freq) {
incumbent->chan_bw_interference_bitmap = 0;
incumbent->handling_in_progress = false;
ath12k_dbg(ab, ATH12K_DBG_MAC,
"incumbent signal interference chan switch completed\n");
} else {
ath12k_warn(ab,
"incumbent signal interference chan switch not done, freq %u\n",
incumbent->center_freq);
}
} else {
if (incumbent->center_freq !=
chandef->chan->center_freq ||
incumbent->width != chandef->width) {
incumbent->chan_bw_interference_bitmap = 0;
incumbent->handling_in_progress = false;
ath12k_dbg(ab, ATH12K_DBG_MAC,
"Bandwidth/channel change due to incumbent signal interference completed\n");
} else {
ath12k_warn(ab, "Bandwidth/channel change due to incumbent sig intf not done intf_freq %u chan_freq %u intf_width %u chan_width %u\n",
incumbent->center_freq,
chandef->chan->center_freq,
incumbent->width,
chandef->width);
}
}
}
spin_unlock_bh(&ar->data_lock);
}
static void
@ -13739,19 +13786,13 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
scan_time_msec = hw->wiphy->max_remain_on_channel_duration * 2;
struct ath12k_wmi_scan_req_arg *arg __free(kfree) =
kzalloc_obj(*arg);
kzalloc_flex(*arg, chan_list, 1);
if (!arg)
return -ENOMEM;
ath12k_wmi_start_scan_init(ar, arg);
arg->num_chan = 1;
ath12k_wmi_start_scan_init(ar, arg);
u32 *chan_list __free(kfree) = kcalloc(arg->num_chan, sizeof(*chan_list),
GFP_KERNEL);
if (!chan_list)
return -ENOMEM;
arg->chan_list = chan_list;
arg->vdev_id = arvif->vdev_id;
arg->scan_id = ATH12K_SCAN_ID;
arg->chan_list[0] = chan->center_freq;
@ -13904,6 +13945,26 @@ static int ath12k_mac_update_band(struct ath12k *ar,
return 0;
}
static void ath12k_mac_update_5_9_ghz_ch_list(struct ath12k *ar,
struct ieee80211_supported_band *band)
{
int i;
if (test_bit(WMI_TLV_SERVICE_5_9GHZ_SUPPORT,
ar->ab->wmi_ab.svc_map))
return;
guard(spinlock_bh)(&ar->ab->base_lock);
if (ar->ab->dfs_region != ATH12K_DFS_REG_FCC)
return;
for (i = 0; i < band->n_channels; i++) {
if (band->channels[i].center_freq >= ATH12K_5_9_GHZ_MIN_FREQ &&
band->channels[i].center_freq <= ATH12K_5_9_GHZ_MAX_FREQ)
band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
}
}
static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
u32 supported_bands,
struct ieee80211_supported_band *bands[])
@ -14037,6 +14098,8 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
band->n_bitrates = ath12k_a_rates_size;
band->bitrates = ath12k_a_rates;
ath12k_mac_update_5_9_ghz_ch_list(ar, band);
if (ab->hw_params->single_pdev_only) {
phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5GHZ_CAP);
reg_cap = &ab->hal_reg_cap[phy_id];
@ -14465,6 +14528,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;
}
@ -14817,6 +14882,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;

View File

@ -1639,7 +1639,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev,
ret = ab_pci->device_family_ops->arch_init(ab);
if (ret) {
ath12k_err(ab, "PCI arch_init failed %d\n", ret);
goto err_pci_msi_free;
goto err_free_irq;
}
ret = ath12k_core_init(ab);

View File

@ -4061,7 +4061,7 @@ int ath12k_qmi_init_service(struct ath12k_base *ab)
spin_lock_init(&ab->qmi.event_lock);
INIT_WORK(&ab->qmi.event_work, ath12k_qmi_driver_event_work);
ret = qmi_add_lookup(&ab->qmi.handle, ATH12K_QMI_WLFW_SERVICE_ID_V01,
ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW,
ATH12K_QMI_WLFW_SERVICE_VERS_V01,
ab->qmi.service_ins_id);
if (ret < 0) {

View File

@ -15,7 +15,6 @@
#define ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE 64
#define ATH12K_QMI_CALDB_ADDRESS 0x4BA00000
#define ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128
#define ATH12K_QMI_WLFW_SERVICE_ID_V01 0x45
#define ATH12K_QMI_WLFW_SERVICE_VERS_V01 0x01
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01 0x02
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1

View File

@ -12,6 +12,137 @@
#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, &param);
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 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, &param);
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)
@ -66,59 +197,108 @@ static struct attribute *ath12k_hwmon_attrs[] = {
};
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;
ar = ab->pdevs[i].ar;
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,
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);
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;
if (!ar)
return;
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)
{
struct ath12k *ar;
int i, j, ret;
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);
}

View File

@ -7,20 +7,41 @@
#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 */
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;
struct thermal_cooling_device *cdev;
/* Serialize thermal operations and hwmon reads */
struct mutex lock;
u32 throttle_state;
};
#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);
int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state);
#else
static inline int ath12k_thermal_register(struct ath12k_base *ab)
{
@ -36,5 +57,19 @@ 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)
{
}
static inline int ath12k_thermal_set_throttling(struct ath12k *ar,
u32 throttle_state)
{
return 0;
}
#endif
#endif /* _ATH12K_THERMAL_ */

View File

@ -234,6 +234,68 @@ static const int ath12k_hw_mode_pri_map[] = {
PRIMAP(WMI_HOST_HW_MODE_MAX),
};
/*
* Interference bitmap transform maps used by
* ath12k_wmi_transform_interference_bitmap().
*
* Firmware reports bitmap bits in a primary-based order where:
* - bit 0 is always the primary 20 MHz segment,
* - bit 1 is the adjacent 20 MHz in the same 40 MHz block,
* - bit 2 is the lower 20 MHz segment of the adjacent 40 MHz segment
* - bit 3 is the higher 20 MHz segment of the adjacent 40 MHz segment
* - remaining bits continue outward in 80/160/320 MHz groups.
*
* cfg80211 userspace notification expects absolute frequency order where:
* - bit 0 is the lowest-frequency 20 MHz segment in the current chandef,
* - bit N increases monotonically toward higher frequency.
*
* For each bandwidth-specific map:
* - row index = primary 20 MHz index in absolute (low->high) order,
* - column index = source bit position from firmware bitmap,
* - value = destination bit position in absolute order bitmap.
*
* Example for 80 MHz: if primary index is 2 (third 20 MHz chunk from low
* frequency), row intf_map_80[2] = { 2, 3, 0, 1 } means firmware bits {0,1,2,3}
* are remapped to destination bits {2,3,0,1} before notifying cfg80211.
*/
static const int intf_map_80[4][4] = {
{ 0, 1, 2, 3 },
{ 1, 0, 2, 3 },
{ 2, 3, 0, 1 },
{ 3, 2, 0, 1 }
};
static const int intf_map_160[8][8] = {
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 1, 0, 2, 3, 4, 5, 6, 7 },
{ 2, 3, 0, 1, 4, 5, 6, 7 },
{ 3, 2, 0, 1, 4, 5, 6, 7 },
{ 4, 5, 6, 7, 0, 1, 2, 3 },
{ 5, 4, 6, 7, 0, 1, 2, 3 },
{ 6, 7, 4, 5, 0, 1, 2, 3 },
{ 7, 6, 4, 5, 0, 1, 2, 3 }
};
static const int intf_map_320[16][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 3, 2, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 5, 4, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 6, 7, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 7, 6, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 9, 8, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 10, 11, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 11, 10, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 12, 13, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 13, 12, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 14, 15, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 },
{ 15, 14, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }
};
static int
ath12k_wmi_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
int (*iter)(struct ath12k_base *ab, u16 tag, u16 len,
@ -3380,6 +3442,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)
{
@ -4199,12 +4330,9 @@ int ath12k_wmi_pdev_lro_cfg(struct ath12k *ar,
if (ret) {
ath12k_warn(ar->ab,
"failed to send lro cfg req wmi cmd\n");
goto err;
dev_kfree_skb(skb);
}
return 0;
err:
dev_kfree_skb(skb);
return ret;
}
@ -4335,12 +4463,9 @@ int ath12k_wmi_vdev_spectral_conf(struct ath12k *ar,
if (ret) {
ath12k_warn(ar->ab,
"failed to send spectral scan config wmi cmd\n");
goto err;
dev_kfree_skb(skb);
}
return 0;
err:
dev_kfree_skb(skb);
return ret;
}
@ -4372,12 +4497,9 @@ int ath12k_wmi_vdev_spectral_enable(struct ath12k *ar, u32 vdev_id,
if (ret) {
ath12k_warn(ar->ab,
"failed to send spectral enable wmi cmd\n");
goto err;
dev_kfree_skb(skb);
}
return 0;
err:
dev_kfree_skb(skb);
return ret;
}
@ -4418,12 +4540,9 @@ int ath12k_wmi_pdev_dma_ring_cfg(struct ath12k *ar,
if (ret) {
ath12k_warn(ar->ab,
"failed to send dma ring cfg req wmi cmd\n");
goto err;
dev_kfree_skb(skb);
}
return 0;
err:
dev_kfree_skb(skb);
return ret;
}
@ -8540,6 +8659,330 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
ev->ctl_failsafe_status);
}
static int
ath12k_wmi_incumbent_signal_interference_subtlv_parser(struct ath12k_base *ab,
u16 tag, u16 len,
const void *ptr,
void *data)
{
const struct ath12k_wmi_incumbent_signal_interference_params *info;
struct ath12k_wmi_incumbent_signal_interference_arg *arg = data;
switch (tag) {
case WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE:
if (len < sizeof(*info)) {
ath12k_warn(ab,
"DCS incumbent signal interference subtlv 0x%x invalid len %u\n",
tag, len);
return -EINVAL;
}
info = ptr;
arg->chan_width = le32_to_cpu(info->chan_width);
arg->chan_freq = le32_to_cpu(info->chan_freq);
arg->center_freq0 = le32_to_cpu(info->center_freq0);
arg->center_freq1 = le32_to_cpu(info->center_freq1);
arg->chan_bw_interference_bitmap =
le32_to_cpu(info->chan_bw_interference_bitmap);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"incumbent signal interference chan width %u freq %u center_freq0 %u center_freq1 %u bitmap 0x%x\n",
arg->chan_width, arg->chan_freq,
arg->center_freq0, arg->center_freq1,
arg->chan_bw_interference_bitmap);
break;
default:
ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference in subtlvs\n",
tag);
return -EINVAL;
}
return 0;
}
static int ath12k_wmi_dcs_interference_event_parser(struct ath12k_base *ab,
u16 tag, u16 len,
const void *ptr, void *data)
{
int ret = 0;
switch (tag) {
case WMI_TAG_DCS_INTERFERENCE_EVENT:
/* Fixed param should already be processed */
break;
case WMI_TAG_ARRAY_STRUCT:
ret = ath12k_wmi_tlv_iter(ab, ptr, len,
ath12k_wmi_incumbent_signal_interference_subtlv_parser,
data);
break;
default:
ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference event\n",
tag);
ret = -EINVAL;
break;
}
return ret;
}
static bool
ath12k_wmi_validate_interference_info(struct ath12k *ar,
struct ath12k_wmi_incumbent_signal_interference_arg *info)
{
switch (info->chan_width) {
case WMI_CHAN_WIDTH_20:
if (info->chan_bw_interference_bitmap > ATH12K_WMI_DCS_SEG_PRI20) {
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"DCS interference event received with wrong chan width bmap 0x%x for 20 MHz",
info->chan_bw_interference_bitmap);
return false;
}
break;
case WMI_CHAN_WIDTH_40:
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
ATH12K_WMI_DCS_SEG_SEC20)) {
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"DCS interference event received with wrong chan width bmap 0x%x for 40 MHz",
info->chan_bw_interference_bitmap);
return false;
}
break;
case WMI_CHAN_WIDTH_80:
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
ATH12K_WMI_DCS_SEG_SEC20 |
ATH12K_WMI_DCS_SEG_SEC40)) {
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"DCS interference event received with wrong chan width bmap 0x%x for 80 MHz",
info->chan_bw_interference_bitmap);
return false;
}
break;
case WMI_CHAN_WIDTH_160:
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
ATH12K_WMI_DCS_SEG_SEC20 |
ATH12K_WMI_DCS_SEG_SEC40 |
ATH12K_WMI_DCS_SEG_SEC80)) {
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"DCS interference event received with wrong chan width bmap 0x%x for 160 MHz",
info->chan_bw_interference_bitmap);
return false;
}
break;
case WMI_CHAN_WIDTH_320:
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
ATH12K_WMI_DCS_SEG_SEC20 |
ATH12K_WMI_DCS_SEG_SEC40 |
ATH12K_WMI_DCS_SEG_SEC80 |
ATH12K_WMI_DCS_SEG_SEC160)) {
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"DCS interference event received with wrong chan width bmap 0x%x for 320 MHz",
info->chan_bw_interference_bitmap);
return false;
}
break;
default:
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"DCS interference event received with unknown channel width %u",
info->chan_width);
return false;
}
return true;
}
static u32
ath12k_wmi_transform_interference_bitmap(int input_bitmap,
struct cfg80211_chan_def *chandef)
{
u16 output_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {};
u16 input_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {};
u32 start_freq, segment_freq;
int primary_index = -1;
u32 output_bitmap = 0;
u16 num_sub_chans;
int bandwidth;
bandwidth = nl80211_chan_width_to_mhz(chandef->width);
if (bandwidth < 0)
return 0;
/*
* Firmware reports bit 0 as primary 20 MHz irrespective of absolute
* frequency position. Convert to standardized lowest-to-highest 20 MHz
* ordering expected by cfg80211/mac80211 userspace consumers.
*/
num_sub_chans = bandwidth / 20;
start_freq = (chandef->center_freq1 - bandwidth / 2) + 10;
for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; i++) {
segment_freq = start_freq + (i * 20);
if (segment_freq == chandef->chan->center_freq) {
primary_index = i;
break;
}
}
if (primary_index == -1)
return 0;
for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i)
input_bits[i] = BIT(i) & input_bitmap;
for (int i = 0; i < num_sub_chans; ++i) {
int src = i, dst = i;
switch (bandwidth) {
case 40:
if (primary_index == 1)
dst = 1 - i;
break;
case 80:
dst = intf_map_80[primary_index][i];
break;
case 160:
dst = intf_map_160[primary_index][i];
break;
case 320:
dst = intf_map_320[primary_index][i];
break;
}
output_bits[dst] = input_bits[src];
}
for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i)
output_bitmap |= output_bits[i] ? BIT(i) : 0;
return output_bitmap;
}
static void
ath12k_wmi_process_incumbent_signal_interference_evt(struct ath12k_base *ab,
struct sk_buff *skb,
const struct ath12k_wmi_intf_arg *intf_arg)
{
struct ath12k_wmi_incumbent_signal_interference_arg info = {};
struct ath12k_incumbent_signal_interference *incumbent;
struct ath12k_mac_get_any_chanctx_conf_arg arg;
u32 transformed_intf_bitmap;
struct ieee80211_hw *hw;
struct ath12k *ar;
int ret;
guard(rcu)();
ar = ath12k_mac_get_ar_by_pdev_id(ab, intf_arg->pdev_id);
if (!ar) {
ath12k_warn(ab, "incumbent signal interference detected on invalid pdev %d\n",
intf_arg->pdev_id);
return;
}
if (!ar->supports_6ghz) {
ath12k_warn(ab, "pdev does not support 6 GHz, dropping DCS interference event\n");
return;
}
incumbent = &ar->incumbent_signal_interference;
spin_lock_bh(&ar->data_lock);
if (incumbent->handling_in_progress) {
spin_unlock_bh(&ar->data_lock);
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"incumbent signal interference handling ongoing, dropping DCS interference event");
return;
}
spin_unlock_bh(&ar->data_lock);
ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
ath12k_wmi_dcs_interference_event_parser,
&info);
if (ret) {
ath12k_warn(ab,
"failed to parse incumbent signal interference TLV. Error %d\n",
ret);
return;
}
if (!ath12k_wmi_validate_interference_info(ar, &info)) {
ath12k_warn(ab, "invalid DCS incumbent signal interference TLV - Skipping event");
return;
}
arg.ar = ar;
arg.chanctx_conf = NULL;
hw = ath12k_ar_to_hw(ar);
ieee80211_iter_chan_contexts_atomic(hw,
ath12k_mac_get_any_chanctx_conf_iter,
&arg);
if (!arg.chanctx_conf) {
ath12k_warn(ab, "failed to find valid chanctx_conf in incumbent signal intf detected event\n");
return;
}
if (info.chan_freq != arg.chanctx_conf->def.chan->center_freq) {
ath12k_dbg(ab, ATH12K_DBG_WMI,
"dcs interference event received with wrong channel %d (ctx freq %d)",
info.chan_freq, arg.chanctx_conf->def.chan->center_freq);
return;
}
spin_lock_bh(&ar->data_lock);
incumbent->center_freq = arg.chanctx_conf->def.chan->center_freq;
incumbent->width = arg.chanctx_conf->def.width;
incumbent->chan_bw_interference_bitmap = info.chan_bw_interference_bitmap;
incumbent->handling_in_progress = true;
spin_unlock_bh(&ar->data_lock);
transformed_intf_bitmap =
ath12k_wmi_transform_interference_bitmap(info.chan_bw_interference_bitmap,
&arg.chanctx_conf->def);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"incumbent signal interference bitmap 0x%x (transformed 0x%x)\n",
info.chan_bw_interference_bitmap, transformed_intf_bitmap);
cfg80211_incumbent_signal_notify(hw->wiphy,
&arg.chanctx_conf->def,
transformed_intf_bitmap,
GFP_ATOMIC);
}
static void
ath12k_wmi_dcs_interference_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
const struct ath12k_wmi_dcs_interference_ev_fixed_params *dcs_intf_ev;
struct ath12k_wmi_intf_arg dcs_intf_arg;
const struct wmi_tlv *tlv;
u16 tlv_tag;
u8 *ptr;
if (skb->len < (sizeof(*dcs_intf_ev) + TLV_HDR_SIZE)) {
ath12k_warn(ab, "DCS interference event is of incorrect length\n");
return;
}
ptr = skb->data;
tlv = (struct wmi_tlv *)ptr;
tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG);
ptr += sizeof(*tlv);
if (tlv_tag != WMI_TAG_DCS_INTERFERENCE_EVENT) {
ath12k_warn(ab, "DCS interference event received with wrong tag\n");
return;
}
dcs_intf_ev = (struct ath12k_wmi_dcs_interference_ev_fixed_params *)ptr;
dcs_intf_arg.interference_type =
le32_to_cpu(dcs_intf_ev->interference_type);
dcs_intf_arg.pdev_id = le32_to_cpu(dcs_intf_ev->pdev_id);
if (dcs_intf_arg.interference_type ==
ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE) {
ath12k_dbg(ab, ATH12K_DBG_WMI,
"incumbent signal interference (Type %u) detected on pdev %u.",
dcs_intf_arg.interference_type,
dcs_intf_arg.pdev_id);
ath12k_wmi_process_incumbent_signal_interference_evt(ab, skb,
&dcs_intf_arg);
}
}
static void
ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab,
const struct ath12k_wmi_pdev_csa_event *ev,
@ -8762,6 +9205,42 @@ 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;
tb = ath12k_wmi_tlv_parse(ab, skb);
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)
{
@ -9811,6 +10290,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;
@ -9865,6 +10347,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
ath12k_wmi_obss_color_collision_event(ab, skb);
break;
case WMI_DCS_INTERFERENCE_EVENTID:
ath12k_wmi_dcs_interference_event(ab, skb);
break;
/* add Unsupported events (rare) here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
@ -10083,6 +10568,42 @@ int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
return ret;
}
int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar,
u32 chan_bw_interference_bitmap)
{
struct wmi_unit_test_arg wmi_ut = {};
struct ath12k_link_vif *arvif;
struct ath12k_vif *ahvif;
bool arvif_found = false;
list_for_each_entry(arvif, &ar->arvifs, list) {
ahvif = arvif->ahvif;
if (arvif->is_started && ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
arvif_found = true;
break;
}
}
if (!arvif_found)
return -EINVAL;
wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF] =
ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE;
wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP] =
chan_bw_interference_bitmap;
wmi_ut.vdev_id = arvif->vdev_id;
wmi_ut.module_id = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE;
wmi_ut.num_args = ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS;
wmi_ut.diag_token = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN;
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"Triggering incumbent signal interference simulation, interference bitmap: 0x%x\n",
chan_bw_interference_bitmap);
return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut);
}
int ath12k_wmi_connect(struct ath12k_base *ab)
{
u32 i;
@ -10874,10 +11395,9 @@ int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params
ath12k_warn(ar->ab, "failed to submit WMI_MLO_SETUP_CMDID command: %d\n",
ret);
dev_kfree_skb(skb);
return ret;
}
return 0;
return ret;
}
int ath12k_wmi_mlo_ready(struct ath12k *ar)
@ -10902,10 +11422,9 @@ int ath12k_wmi_mlo_ready(struct ath12k *ar)
ath12k_warn(ar->ab, "failed to submit WMI_MLO_READY_CMDID command: %d\n",
ret);
dev_kfree_skb(skb);
return ret;
}
return 0;
return ret;
}
int ath12k_wmi_mlo_teardown(struct ath12k *ar)
@ -10931,10 +11450,9 @@ int ath12k_wmi_mlo_teardown(struct ath12k *ar)
ath12k_warn(ar->ab, "failed to submit WMI MLO teardown command: %d\n",
ret);
dev_kfree_skb(skb);
return ret;
}
return 0;
return ret;
}
bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar)
@ -10997,10 +11515,9 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
if (ret) {
ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n");
dev_kfree_skb(skb);
return ret;
}
return 0;
return ret;
}
static int

View File

@ -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,
@ -2014,7 +2015,7 @@ enum wmi_tlv_tag {
WMI_TAG_VDEV_CH_POWER_INFO,
WMI_TAG_MLO_LINK_SET_ACTIVE_CMD = 0x3BE,
WMI_TAG_EHT_RATE_SET = 0x3C4,
WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5,
WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE = 0x3C5,
WMI_TAG_MLO_TX_SEND_PARAMS,
WMI_TAG_MLO_PARTNER_LINK_PARAMS,
WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC,
@ -2259,6 +2260,7 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219,
WMI_TLV_SERVICE_EXT2_MSG = 220,
WMI_TLV_SERVICE_BEACON_PROTECTION_SUPPORT = 244,
WMI_TLV_SERVICE_5_9GHZ_SUPPORT = 247,
WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249,
WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253,
@ -2268,11 +2270,17 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT = 286,
WMI_TLV_SERVICE_11BE = 289,
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,
@ -3586,7 +3594,6 @@ struct ath12k_wmi_scan_req_arg {
u32 num_bssid;
u32 num_ssids;
u32 n_probes;
u32 *chan_list;
u32 notify_scan_events;
struct cfg80211_ssid ssid[WLAN_SCAN_MAX_NUM_SSID];
struct ath12k_wmi_mac_addr_params bssid_list[WLAN_SCAN_MAX_NUM_BSSID];
@ -3595,6 +3602,7 @@ struct ath12k_wmi_scan_req_arg {
u32 num_hint_bssid;
struct ath12k_wmi_hint_short_ssid_arg hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID];
struct ath12k_wmi_hint_bssid_arg hint_bssid[WLAN_SCAN_MAX_HINT_BSSID];
u32 chan_list[] __counted_by(num_chan);
};
struct wmi_ssid_arg {
@ -4120,6 +4128,49 @@ 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;
#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;
@ -4203,6 +4254,16 @@ enum dfs_test_args_idx {
DFS_MAX_TEST_ARGS,
};
#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE 0x18
#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN 0
#define ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE 1
enum ath12k_wmi_incumbent_signal_test_args_idx {
ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF,
ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP,
ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS,
};
/* update if another test command requires more */
#define WMI_UNIT_TEST_ARGS_MAX DFS_MAX_TEST_ARGS
@ -4487,6 +4548,62 @@ struct ath12k_wmi_pdev_radar_event {
a_sle32 sidx;
} __packed;
#define ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE 0x04
struct ath12k_wmi_dcs_interference_ev_fixed_params {
__le32 interference_type;
__le32 pdev_id;
} __packed;
struct ath12k_wmi_incumbent_signal_interference_params {
__le32 chan_width;
__le32 chan_freq;
__le32 center_freq0;
__le32 center_freq1;
__le32 chan_bw_interference_bitmap;
} __packed;
struct ath12k_wmi_incumbent_signal_interference_arg {
u32 chan_width;
u32 chan_freq;
u32 center_freq0;
u32 center_freq1;
u32 chan_bw_interference_bitmap;
};
struct ath12k_wmi_intf_arg {
u32 interference_type;
u32 pdev_id;
};
enum ath12k_wmi_dcs_interference_chan_segment {
/*
* Firmware reports interference bitmap in primary-based order.
* Bit 0 is the primary 20 MHz, bit 1 is the adjacent 20 MHz within
* the primary 40 MHz. Bits 2-3 cover the secondary 40 MHz, bits 4-7
* cover the secondary 80 MHz, and bits 8-15 cover the secondary 160 MHz.
*/
ATH12K_WMI_DCS_SEG_PRI20 = 0x1,
ATH12K_WMI_DCS_SEG_SEC20 = 0x2,
ATH12K_WMI_DCS_SEG_SEC40_LOW = 0x4,
ATH12K_WMI_DCS_SEG_SEC40_UP = 0x8,
ATH12K_WMI_DCS_SEG_SEC40 = 0xC,
ATH12K_WMI_DCS_SEG_SEC80_LOW = 0x10,
ATH12K_WMI_DCS_SEG_SEC80_LOW_UP = 0x20,
ATH12K_WMI_DCS_SEG_SEC80_UP_LOW = 0x40,
ATH12K_WMI_DCS_SEG_SEC80_UP = 0x80,
ATH12K_WMI_DCS_SEG_SEC80 = 0xF0,
ATH12K_WMI_DCS_SEG_SEC160_LOW = 0x0100,
ATH12K_WMI_DCS_SEG_SEC160_LOW_UP = 0x0200,
ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP = 0x0400,
ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP_UP = 0x0800,
ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW_LOW = 0x1000,
ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW = 0x2000,
ATH12K_WMI_DCS_SEG_SEC160_UP_LOW = 0x4000,
ATH12K_WMI_DCS_SEG_SEC160_UP = 0x8000,
ATH12K_WMI_DCS_SEG_SEC160 = 0xFF00,
};
struct wmi_pdev_temperature_event {
/* temperature value in Celsius degree */
a_sle32 temp;
@ -6514,6 +6631,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)
@ -6576,6 +6695,8 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
struct ath12k_reg_tpc_power_info *param);
int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
struct wmi_mlo_link_set_active_arg *param);
int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar,
u32 chan_bw_interference_bitmap);
int ath12k_wmi_alloc(void);
void ath12k_wmi_free(void);

View File

@ -80,14 +80,14 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
/* iniAddac needs to be modified for these chips */
if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) {
struct ar5416IniArray *addac = &ah->iniAddac;
u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns;
u32 n = addac->ia_rows * addac->ia_columns;
u32 *data;
data = devm_kzalloc(ah->dev, size, GFP_KERNEL);
data = devm_kmemdup_array(ah->dev, addac->ia_array, n, sizeof(u32),
GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(data, addac->ia_array, size);
addac->ia_array = data;
if (!AR_SREV_5416_22_OR_LATER(ah)) {

View File

@ -137,24 +137,6 @@ static void owl_fw_cb(const struct firmware *fw, void *context)
release_firmware(fw);
}
static const char *owl_get_eeprom_name(struct pci_dev *pdev)
{
struct device *dev = &pdev->dev;
char *eeprom_name;
dev_dbg(dev, "using auto-generated eeprom filename\n");
eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
if (!eeprom_name)
return NULL;
/* this should match the pattern used in ath9k/init.c */
scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
dev_name(dev));
return eeprom_name;
}
static void owl_nvmem_work(struct work_struct *work)
{
struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
@ -195,8 +177,9 @@ static int owl_nvmem_probe(struct owl_ctx *ctx)
static int owl_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
char eeprom_name[EEPROM_FILENAME_LEN];
struct device *dev = &pdev->dev;
struct owl_ctx *ctx;
const char *eeprom_name;
int err = 0;
if (pci_enable_device(pdev))
@ -215,11 +198,11 @@ static int owl_probe(struct pci_dev *pdev,
if (err <= 0)
return err;
eeprom_name = owl_get_eeprom_name(pdev);
if (!eeprom_name) {
dev_err(&pdev->dev, "no eeprom filename found.\n");
return -ENODEV;
}
dev_dbg(dev, "using auto-generated eeprom filename\n");
/* this should match the pattern used in ath9k/init.c */
scnprintf(eeprom_name, sizeof(eeprom_name), "ath9k-eeprom-pci-%s.bin",
dev_name(dev));
err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
&pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);

View File

@ -133,13 +133,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
ATH9K_NUM_CHANNELS);
if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
channels = devm_kzalloc(ah->dev,
channels = devm_kmemdup(ah->dev, ath9k_2ghz_chantable,
sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
if (!channels)
return -ENOMEM;
memcpy(channels, ath9k_2ghz_chantable,
sizeof(ath9k_2ghz_chantable));
common->sbands[NL80211_BAND_2GHZ].channels = channels;
common->sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ;
common->sbands[NL80211_BAND_2GHZ].n_channels =
@ -150,13 +148,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
}
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
channels = devm_kzalloc(ah->dev,
channels = devm_kmemdup(ah->dev, ath9k_5ghz_chantable,
sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
if (!channels)
return -ENOMEM;
memcpy(channels, ath9k_5ghz_chantable,
sizeof(ath9k_5ghz_chantable));
common->sbands[NL80211_BAND_5GHZ].channels = channels;
common->sbands[NL80211_BAND_5GHZ].band = NL80211_BAND_5GHZ;
common->sbands[NL80211_BAND_5GHZ].n_channels =

View File

@ -285,7 +285,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u8 *ds;
int i, bsize, desc_len;
int i, desc_len;
ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
name, nbuf, ndesc);
@ -339,8 +339,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
if (is_tx) {
struct ath_buf *bf;
bsize = sizeof(struct ath_buf) * nbuf;
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
bf = devm_kcalloc(sc->dev, sizeof(*bf), nbuf, GFP_KERNEL);
if (!bf)
return -ENOMEM;
@ -370,8 +369,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
} else {
struct ath_rxbuf *bf;
bsize = sizeof(struct ath_rxbuf) * nbuf;
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbuf, GFP_KERNEL);
if (!bf)
return -ENOMEM;
@ -576,7 +574,7 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc)
size_t len;
int err;
cell = devm_nvmem_cell_get(sc->dev, "calibration");
cell = nvmem_cell_get(sc->dev, "calibration");
if (IS_ERR(cell)) {
err = PTR_ERR(cell);
@ -593,6 +591,7 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc)
}
buf = nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
if (IS_ERR(buf))
return PTR_ERR(buf);

View File

@ -202,7 +202,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
struct sk_buff *skb;
struct ath_rxbuf *bf;
int error = 0, i;
u32 size;
ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
ah->caps.rx_status_len);
@ -212,8 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
ah->caps.rx_hp_qdepth);
size = sizeof(struct ath_rxbuf) * nbufs;
bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbufs, GFP_KERNEL);
if (!bf)
return -ENOMEM;

View File

@ -1993,7 +1993,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
ieee80211_txq_schedule_start(hw, txq->mac80211_qnum);
spin_lock_bh(&sc->chan_lock);
rcu_read_lock();
if (sc->cur_chan->stopped)
goto out;
@ -2011,7 +2010,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
}
out:
rcu_read_unlock();
spin_unlock_bh(&sc->chan_lock);
ieee80211_txq_schedule_end(hw, txq->mac80211_qnum);
}
@ -2746,6 +2744,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
continue;
}
if (ts.qid >= ATH9K_NUM_TX_QUEUES) {
ath_dbg(common, XMIT, "invalid qid %d\n", ts.qid);
continue;
}
txq = &sc->tx.txq[ts.qid];
ath_txq_lock(sc, txq);