wlan-ap-Telecominfraproject/feeds/ipq95xx/mac80211/patches/qca/690-0002-ath12k-add-multi-vdev-restart-support.patch
John Crispin 144c5d00f4 ipq95xx/mac80211: update to ATH12.3-CS
Signed-off-by: John Crispin <john@phrozen.org>
2024-02-28 18:56:21 +01:00

662 lines
20 KiB
Diff

From 0b2c0e6cee6ea4770e32df4139b3a887dd49eb80 Mon Sep 17 00:00:00 2001
From: Aditya Kumar Singh <quic_adisi@quicinc.com>
Date: Tue, 29 Nov 2022 16:16:13 +0530
Subject: [PATCH 2/2] ath12k: add multi-vdev restart support
Multi-vdev restart command is used to update the channel to all affected vdevs
at once. Among all vdev params, only channel could vary in this command. Hence,
during channel change, this command is useful.
Currently, during channel change, each vdev is brought down first, then channel
is changed followed by bringing vdev up. In multi-vif scenario, this might lead
to FW buffer overflow. Instead, multi vdev restart comand could be used.
Add support for multi vdev restart command and send it during channel switch.
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
---
drivers/net/wireless/ath/ath12k/core.h | 4 +
drivers/net/wireless/ath/ath12k/mac.c | 189 +++++++++++++++++++-
drivers/net/wireless/ath/ath12k/wmi.c | 235 ++++++++++++++++++++++---
drivers/net/wireless/ath/ath12k/wmi.h | 47 +++++
4 files changed, 448 insertions(+), 27 deletions(-)
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -362,6 +362,8 @@ struct ath12k_link_vif {
struct work_struct update_obss_color_notify_work;
u8 link_id;
struct ath12k_vif *ahvif;
+
+ bool mvr_processing;
};
struct ath12k_vif {
@@ -870,6 +872,8 @@ struct ath12k {
bool fw_stats_done;
unsigned long last_tx_power_update;
+
+ struct completion mvr_complete;
};
struct ath12k_band_cap {
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -9941,12 +9941,19 @@ static int ath12k_vdev_restart_sequence(
memcpy(&arvif->chanctx, new_ctx, sizeof(*new_ctx));
spin_unlock_bh(&ar->data_lock);
- if (vif_down_failed_map & BIT_ULL(vdev_index))
+ /* vdev is already restarted via mvr, need to setup
+ * certain config alone after restart */
+ if (vdev_index == -1) {
+ ret = ath12k_mac_vdev_config_after_start(arvif, &new_ctx->def);
+ if (!ret)
+ goto beacon_tmpl_setup;
+ } else if (vif_down_failed_map & BIT_ULL(vdev_index)) {
ret = ath12k_mac_vdev_restart(arvif, &new_ctx->def, false,
new_ctx->radar_enabled);
- else
+ } else {
ret = ath12k_mac_vdev_restart(arvif, &new_ctx->def, true,
new_ctx->radar_enabled);
+ }
if (ret) {
ath12k_warn(ar->ab, "failed to restart vdev %d: %d\n",
@@ -9957,6 +9964,7 @@ static int ath12k_vdev_restart_sequence(
return ret;
}
+beacon_tmpl_setup:
if (!arvif->is_up)
return -EOPNOTSUPP;
@@ -10211,6 +10219,45 @@ static void ath12k_mac_update_rx_channel
ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
}
+static int
+ath12k_mac_multi_vdev_restart(struct ath12k *ar,
+ const struct cfg80211_chan_def *chandef,
+ u32 *vdev_id, int len,
+ bool radar_enabled)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct wmi_pdev_multiple_vdev_restart_req_arg arg = {};
+ int ret, i;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ arg.vdev_ids.id_len = len;
+
+ for (i = 0; i < len; i++)
+ arg.vdev_ids.id[i] = vdev_id[i];
+
+ arg.channel.freq = chandef->chan->center_freq;
+ arg.channel.band_center_freq1 = chandef->center_freq1;
+ arg.channel.band_center_freq2 = chandef->center_freq2;
+ arg.channel.mode =
+ ath12k_phymodes[chandef->chan->band][chandef->width];
+
+ arg.channel.min_power = 0;
+ arg.channel.max_power = chandef->chan->max_power * 2;
+ arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+ arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
+ arg.channel.chan_radar = !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
+ arg.channel.passive = arg.channel.chan_radar;
+ arg.channel.freq2_radar = radar_enabled;
+ arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR);
+ arg.ru_punct_bitmap = ~chandef->ru_punct_bitmap;
+ ret = ath12k_wmi_pdev_multiple_vdev_restart(ar, &arg);
+ if (ret)
+ ath12k_warn(ab, "mac failed to do mvr (%d)\n", ret);
+
+ return ret;
+}
+
static void
ath12k_mac_update_vif_chan_extras(struct ath12k *ar,
struct ieee80211_vif_chanctx_switch *vifs,
@@ -10369,6 +10416,148 @@ ath12k_mac_update_vif_chan(struct ath12k
arvif->vdev_id, ret);
}
}
+}
+
+static void
+ath12k_mac_update_vif_chan_mvr(struct ath12k *ar,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_link_vif *arvif, *tx_arvif;
+ struct ath12k_vif *ahvif;
+ struct cfg80211_chan_def *chandef;
+ struct ieee80211_vif *tx_vif;
+ int ret, i, time_left, trans_vdev_index, vdev_idx, n_vdevs = 0;
+ u32 vdev_ids[TARGET_NUM_VDEVS];
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ chandef = &vifs[0].new_ctx->def;
+ tx_arvif = NULL;
+
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch via mvr");
+
+ ath12k_mac_update_rx_channel(ar, NULL, vifs, n_vifs);
+
+ for (i = 0; i < n_vifs; i++) {
+ ahvif = (void *)vifs[i].vif->drv_priv;
+ arvif = ahvif->link[vifs[i].link_conf->link_id];
+
+ if (WARN_ON(!arvif))
+ continue;
+
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
+ "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n",
+ arvif->vdev_id,
+ vifs[i].old_ctx->def.chan->center_freq,
+ vifs[i].new_ctx->def.chan->center_freq,
+ vifs[i].old_ctx->def.width,
+ vifs[i].new_ctx->def.width);
+
+ if (!arvif->is_started) {
+ memcpy(&arvif->chanctx, vifs[i].new_ctx, sizeof(*vifs[i].new_ctx));
+ continue;
+ }
+
+ if (ahvif->vif->mbssid_tx_vif &&
+ ahvif == (struct ath12k_vif *)ahvif->vif->mbssid_tx_vif->drv_priv) {
+ tx_vif = ahvif->vif->mbssid_tx_vif;
+ /* TODO ML mbssid changes */
+ tx_arvif = &ath12k_vif_to_ahvif(tx_vif)->deflink;
+ trans_vdev_index = i;
+ }
+
+ arvif->mvr_processing = true;
+ vdev_ids[n_vdevs++] = arvif->vdev_id;
+ }
+
+ if (!n_vdevs) {
+ ath12k_dbg(ab, ATH12K_DBG_MAC,
+ "mac 0 vdevs available to switch chan ctx via mvr\n");
+ return;
+ }
+
+ reinit_completion(&ar->mvr_complete);
+
+ ret = ath12k_mac_multi_vdev_restart(ar, chandef, vdev_ids, n_vdevs,
+ vifs[0].new_ctx->radar_enabled);
+ if (ret) {
+ ath12k_warn(ab, "mac failed to send mvr command (%d)\n", ret);
+ return;
+ }
+
+ time_left = wait_for_completion_timeout(&ar->mvr_complete,
+ WMI_MVR_CMD_TIMEOUT_HZ);
+ if (!time_left) {
+ ath12k_err(ar->ab, "mac mvr cmd response timed out\n");
+ /* fallback to restarting one-by-one */
+ return ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
+ }
+
+ if (tx_arvif) {
+ vdev_idx = -1;
+
+ if (tx_arvif->mvr_processing) {
+ /* failed to restart tx vif via mvr, fallback */
+ arvif->mvr_processing = false;
+ vdev_idx = trans_vdev_index;
+ ath12k_err(ab,
+ "mac failed to restart mbssid tx vdev %d via mvr cmd\n",
+ tx_arvif->vdev_id);
+ }
+
+ ret = ath12k_vdev_restart_sequence(tx_arvif,
+ vifs[trans_vdev_index].new_ctx,
+ BIT_ULL(trans_vdev_index),
+ vdev_idx);
+ if (ret)
+ ath12k_warn(ab,
+ "mac failed to bring up mbssid tx vdev %d after mvr (%d)\n",
+ tx_arvif->vdev_id, ret);
+ }
+
+ for (i = 0; i < n_vifs; i++) {
+ ahvif = (void *)vifs[i].vif->drv_priv;
+ arvif = ahvif->link[vifs[i].link_conf->link_id];
+
+ if (WARN_ON(!arvif))
+ continue;
+
+ vdev_idx = -1;
+
+ if (ahvif->vif->mbssid_tx_vif && arvif == tx_arvif)
+ continue;
+
+ if (arvif->mvr_processing) {
+ /* failed to restart vdev via mvr, fallback */
+ arvif->mvr_processing = false;
+ vdev_idx = i;
+ ath12k_err(ab, "mac failed to restart vdev %d via mvr cmd\n",
+ arvif->vdev_id);
+ }
+
+ ret = ath12k_vdev_restart_sequence(arvif, vifs[i].new_ctx,
+ BIT_ULL(i), vdev_idx);
+ if (ret && ret != -EOPNOTSUPP)
+ ath12k_warn(ab, "mac failed to bring up vdev %d after mvr (%d)\n",
+ arvif->vdev_id, ret);
+ }
+}
+
+static void
+ath12k_mac_process_update_vif_chan(struct ath12k *ar,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs)
+{
+ struct ath12k_base *ab = ar->ab;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (ath12k_wmi_is_mvr_supported(ab))
+ ath12k_mac_update_vif_chan_mvr(ar, vifs, n_vifs);
+ else
+ ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
ath12k_mac_update_vif_chan_extras(ar, vifs, n_vifs);
}
@@ -10400,7 +10589,7 @@ ath12k_mac_update_active_vif_chan(struct
ath12k_mac_change_chanctx_fill_iter,
&arg);
- ath12k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs);
+ ath12k_mac_process_update_vif_chan(ar, arg.vifs, arg.n_vifs);
kfree(arg.vifs);
out:
@@ -11321,7 +11510,7 @@ ath12k_mac_op_switch_vif_chanctx(struct
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
"mac chanctx switch n_vifs %d mode %d\n",
n_vifs, mode);
- ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
+ ath12k_mac_process_update_vif_chan(ar, vifs, n_vifs);
mutex_unlock(&ar->conf_mutex);
@@ -13616,6 +13805,7 @@ static int ath12k_mac_setup(struct ath12
init_completion(&ar->scan.completed);
init_completion(&ar->thermal.wmi_sync);
init_completion(&ar->mlo_setup_done);
+ init_completion(&ar->mvr_complete);
INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work);
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -956,55 +956,55 @@ int ath12k_wmi_vdev_down(struct ath12k *
}
static void ath12k_wmi_put_wmi_channel(struct wmi_channel *chan,
- struct wmi_vdev_start_req_arg *arg)
+ struct wmi_channel_arg chan_arg)
{
- u32 center_freq1 = arg->channel.band_center_freq1;
+ u32 center_freq1 = chan_arg.band_center_freq1;
memset(chan, 0, sizeof(*chan));
- chan->mhz = arg->channel.freq;
- chan->band_center_freq1 = arg->channel.band_center_freq1;
+ chan->mhz = chan_arg.freq;
+ chan->band_center_freq1 = chan_arg.band_center_freq1;
- if ((arg->channel.mode == MODE_11AX_HE160) || (arg->channel.mode == MODE_11BE_EHT160)) {
- if (arg->channel.freq > arg->channel.band_center_freq1)
+ if ((chan_arg.mode == MODE_11AX_HE160) || (chan_arg.mode == MODE_11BE_EHT160)) {
+ if (chan_arg.freq > chan_arg.band_center_freq1)
chan->band_center_freq1 = center_freq1 + 40;
else
chan->band_center_freq1 = center_freq1 - 40;
- chan->band_center_freq2 = arg->channel.band_center_freq1;
+ chan->band_center_freq2 = chan_arg.band_center_freq1;
- } else if (arg->channel.mode == MODE_11AC_VHT80_80) {
- chan->band_center_freq2 = arg->channel.band_center_freq2;
+ } else if (chan_arg.mode == MODE_11AC_VHT80_80) {
+ chan->band_center_freq2 = chan_arg.band_center_freq2;
} else {
chan->band_center_freq2 = 0;
}
- chan->info |= u32_encode_bits(arg->channel.mode, WMI_CHAN_INFO_MODE);
- if (arg->channel.passive)
+ chan->info |= u32_encode_bits(chan_arg.mode, WMI_CHAN_INFO_MODE);
+ if (chan_arg.passive)
chan->info |= WMI_CHAN_INFO_PASSIVE;
- if (arg->channel.allow_ibss)
+ if (chan_arg.allow_ibss)
chan->info |= WMI_CHAN_INFO_ADHOC_ALLOWED;
- if (arg->channel.allow_ht)
+ if (chan_arg.allow_ht)
chan->info |= WMI_CHAN_INFO_ALLOW_HT;
- if (arg->channel.allow_vht)
+ if (chan_arg.allow_vht)
chan->info |= WMI_CHAN_INFO_ALLOW_VHT;
- if (arg->channel.allow_he)
+ if (chan_arg.allow_he)
chan->info |= WMI_CHAN_INFO_ALLOW_HE;
- if (arg->channel.ht40plus)
+ if (chan_arg.ht40plus)
chan->info |= WMI_CHAN_INFO_HT40_PLUS;
- if (arg->channel.chan_radar)
+ if (chan_arg.chan_radar)
chan->info |= WMI_CHAN_INFO_DFS;
- if (arg->channel.freq2_radar)
+ if (chan_arg.freq2_radar)
chan->info |= WMI_CHAN_INFO_DFS_FREQ2;
- chan->reg_info_1 = u32_encode_bits(arg->channel.max_power,
+ chan->reg_info_1 = u32_encode_bits(chan_arg.max_power,
WMI_CHAN_REG_INFO1_MAX_PWR) |
- u32_encode_bits(arg->channel.max_reg_power,
+ u32_encode_bits(chan_arg.max_reg_power,
WMI_CHAN_REG_INFO1_MAX_REG_PWR);
- chan->reg_info_2 = u32_encode_bits(arg->channel.max_antenna_gain,
+ chan->reg_info_2 = u32_encode_bits(chan_arg.max_antenna_gain,
WMI_CHAN_REG_INFO2_ANT_MAX) |
- u32_encode_bits(arg->channel.max_power, WMI_CHAN_REG_INFO2_MAX_TX_PWR);
+ u32_encode_bits(chan_arg.max_power, WMI_CHAN_REG_INFO2_MAX_TX_PWR);
}
int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg,
@@ -1067,7 +1067,7 @@ int ath12k_wmi_vdev_start(struct ath12k
ptr = skb->data + sizeof(*cmd);
chan = ptr;
- ath12k_wmi_put_wmi_channel(chan, arg);
+ ath12k_wmi_put_wmi_channel(chan, arg->channel);
chan->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CHANNEL,
sizeof(*chan));
@@ -9471,6 +9471,108 @@ static void ath12k_wmi_event_teardown_co
kfree(tb);
}
+static void ath12k_wmi_process_mvr_event(struct ath12k *ab, u32 *vdev_id_bm,
+ u32 num_vdev_bm)
+{
+ struct ath12k *ar = NULL;
+ struct ath12k_link_vif *arvif = NULL;
+ u32 vdev_bitmap, bit_pos;
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "wmi mvr resp num_vdev_bm %d vdev_id_bm[0]=0x%x vdev_id_bm[1]=0x%x\n",
+ num_vdev_bm, vdev_id_bm[0],
+ (num_vdev_bm == WMI_MVR_RESP_VDEV_BM_MAX_LEN ?
+ vdev_id_bm[1] : 0x00));
+
+ /* 31-0 bits processing */
+ vdev_bitmap = vdev_id_bm[0];
+
+ for (bit_pos = 0; bit_pos < 32; bit_pos++) {
+
+ if (!test_bit(bit_pos, &vdev_bitmap))
+ continue;
+
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, bit_pos);
+ if (!arvif) {
+ ath12k_warn(ab, "wmi mvr resp for unknown vdev %d", bit_pos);
+ continue;
+ }
+
+ arvif->mvr_processing = false;
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "wmi mvr vdev %d restarted\n", bit_pos);
+ }
+
+ /* TODO: 63-32 bits processing
+ * Add support to parse bitmap once support for
+ * TARGET_NUM_VDEVS > 32 is added
+ */
+
+ if (arvif)
+ ar = arvif->ar;
+
+ if (ar)
+ complete(&ar->mvr_complete);
+}
+
+static int ath12k_wmi_tlv_mvr_event_parse(struct ath12k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_pdev_mvr_resp_event_parse *parse = data;
+ struct wmi_pdev_mvr_resp_event_fixed_param *fixed_param;
+
+ switch(tag) {
+ case WMI_TAG_MULTIPLE_VDEV_RESTART_RESPONSE_EVENT:
+ fixed_param = (struct wmi_pdev_mvr_resp_event_fixed_param *)ptr;
+
+ if (fixed_param->status) {
+ ath12k_warn(ab, "wmi mvr resp event status %u\n",
+ fixed_param->status);
+ return -EINVAL;
+ }
+
+ memcpy(&parse->fixed_param, fixed_param,
+ sizeof(struct wmi_pdev_mvr_resp_event_fixed_param));
+ break;
+ case WMI_TAG_ARRAY_UINT32:
+ if ((len > WMI_MVR_RESP_VDEV_BM_MAX_LEN_BYTES) || (len == 0)) {
+ ath12k_warn(ab, "wmi invalid vdev id len in mvr resp %u\n",
+ len);
+ return -EINVAL;
+ }
+
+ parse->num_vdevs_bm = len / sizeof(u32);
+ memcpy(parse->vdev_id_bm, ptr, len);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void ath12k_wmi_event_mvr_response(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ struct wmi_pdev_mvr_resp_event_parse parse = {};
+ int ret;
+
+ ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath12k_wmi_tlv_mvr_event_parse,
+ &parse);
+ if (ret) {
+ ath12k_warn(ab, "wmi failed to parse mvr response tlv %d\n",
+ ret);
+ return;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi mvr resp for pdev %d\n",
+ parse.fixed_param.pdev_id);
+
+ ath12k_wmi_process_mvr_event(ab, parse.vdev_id_bm, parse.num_vdevs_bm);
+}
+
static void ath12k_wmi_tlv_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
@@ -9617,6 +9719,9 @@ static void ath12k_wmi_tlv_op_rx(struct
case WMI_MLO_TEARDOWN_COMPLETE_EVENTID:
ath12k_wmi_event_teardown_complete(ab, skb);
break;
+ case WMI_PDEV_MULTIPLE_VDEV_RESTART_RESP_EVENTID:
+ ath12k_wmi_event_mvr_response(ab, skb);
+ break;
/* TODO: Add remaining events */
default:
ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
@@ -10073,3 +10178,82 @@ int ath12k_wmi_mlo_teardown(struct ath12
return ret;
}
+
+bool ath12k_wmi_is_mvr_supported(struct ath12k_base *ab)
+{
+ struct ath12k_wmi_base *wmi_ab = &ab->wmi_ab;
+
+ return test_bit(WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART,
+ wmi_ab->svc_map) &&
+ test_bit(WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART_RESPONSE_SUPPORT,
+ wmi_ab->svc_map);
+}
+
+int ath12k_wmi_pdev_multiple_vdev_restart(struct ath12k *ar,
+ struct wmi_pdev_multiple_vdev_restart_req_arg *arg)
+{
+ struct ath12k_pdev_wmi *wmi = ar->wmi;
+ struct wmi_pdev_multiple_vdev_restart_request_cmd *cmd;
+ struct wmi_channel *chan;
+ struct wmi_tlv *tlv;
+ u32 num_vdev_ids;
+ __le32 *vdev_ids;
+ size_t vdev_ids_len;
+ struct sk_buff *skb;
+ void *ptr;
+ int ret, len, i;
+
+ if (WARN_ON(arg->vdev_ids.id_len > TARGET_NUM_VDEVS))
+ return -EINVAL;
+
+ num_vdev_ids = arg->vdev_ids.id_len;
+ vdev_ids_len = num_vdev_ids * sizeof(__le32);
+
+ len = sizeof(*cmd) + TLV_HDR_SIZE + vdev_ids_len +
+ sizeof(*chan);
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_pdev_multiple_vdev_restart_request_cmd *)skb->data;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMD,
+ sizeof(*cmd));
+ cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
+ cmd->num_vdevs = cpu_to_le32(arg->vdev_ids.id_len);
+ cmd->puncture_20mhz_bitmap = cpu_to_le32(arg->ru_punct_bitmap);
+
+ cmd->flags = cpu_to_le32(WMI_MVR_RESPONSE_SUPPORT_EXPECTED);
+
+ ptr = skb->data + sizeof(*cmd);
+ tlv = (struct wmi_tlv *)ptr;
+
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, vdev_ids_len);
+ vdev_ids = (__le32 *)tlv->value;
+
+ for (i = 0; i < num_vdev_ids; i++)
+ vdev_ids[i] = cpu_to_le32(arg->vdev_ids.id[i]);
+
+ ptr += TLV_HDR_SIZE + vdev_ids_len;
+ chan = (struct wmi_channel *)ptr;
+
+ ath12k_wmi_put_wmi_channel(chan, arg->channel);
+
+ chan->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CHANNEL, sizeof(*chan));
+ ptr += sizeof(*chan);
+
+ ret = ath12k_wmi_cmd_send(wmi, skb,
+ WMI_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "wmi failed to send mvr command (%d)\n",
+ ret);
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "wmi mvr cmd sent num_vdevs %d freq %d\n",
+ num_vdev_ids, arg->channel.freq);
+
+ return ret;
+}
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -1919,6 +1919,7 @@ enum wmi_tlv_tag {
WMI_TAG_MUEDCA_PARAMS_CONFIG_EVENT = 0x32a,
WMI_TAG_SERVICE_READY_EXT2_EVENT = 0x334,
WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344,
+ WMI_TAG_MULTIPLE_VDEV_RESTART_RESPONSE_EVENT = 0x365,
WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F,
WMI_TAG_PDEV_SRG_BSS_COLOR_BITMAP_CMD = 0x37b,
WMI_TAG_PDEV_SRG_PARTIAL_BSSID_BITMAP_CMD,
@@ -2176,6 +2177,7 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213,
WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219,
WMI_TLV_SERVICE_EXT2_MSG = 220,
+ WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART_RESPONSE_SUPPORT = 235,
WMI_TLV_SERVICE_5_9GHZ_SUPPORT = 247,
WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249,
@@ -6354,6 +6356,47 @@ struct wmi_mlo_teardown_complete_fixed_p
u32 status;
} __packed;
+/* Inform FW that host expects response for multi-vdev
+ * restart command */
+#define WMI_MVR_RESPONSE_SUPPORT_EXPECTED 0x1
+#define WMI_MVR_CMD_TIMEOUT_HZ (2 * HZ)
+#define WMI_MVR_RESP_VDEV_BM_MAX_LEN 2
+#define WMI_MVR_RESP_VDEV_BM_MAX_LEN_BYTES (WMI_MVR_RESP_VDEV_BM_MAX_LEN * 4)
+
+struct wmi_vdev_ids_arg {
+ u32 id_len;
+ u32 id[17]; /* TARGET_NUM_VDEVS */
+};
+
+struct wmi_pdev_multiple_vdev_restart_req_arg {
+ struct wmi_vdev_ids_arg vdev_ids;
+ struct wmi_channel_arg channel;
+ u16 ru_punct_bitmap;
+};
+
+struct wmi_pdev_multiple_vdev_restart_request_cmd {
+ __le32 tlv_header;
+ __le32 pdev_id;
+ __le32 requestor_id;
+ __le32 disable_hw_ack;
+ __le32 cac_duration_ms;
+ __le32 num_vdevs;
+ __le32 flags;
+ __le32 puncture_20mhz_bitmap;
+} __packed;
+
+struct wmi_pdev_mvr_resp_event_fixed_param {
+ u32 pdev_id;
+ u32 requestor_id;
+ u32 status;
+} __packed;
+
+struct wmi_pdev_mvr_resp_event_parse {
+ struct wmi_pdev_mvr_resp_event_fixed_param fixed_param;
+ u32 num_vdevs_bm;
+ u32 vdev_id_bm[];
+} __packed;
+
#define ATH12K_FW_STATS_BUF_SIZE (1024 * 1024)
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
@@ -6522,4 +6565,7 @@ int ath12k_wmi_mlo_setup(struct ath12k *
struct wmi_mlo_setup_params *mlo_params);
int ath12k_wmi_mlo_ready(struct ath12k *ar);
int ath12k_wmi_mlo_teardown(struct ath12k *ar);
+bool ath12k_wmi_is_mvr_supported(struct ath12k_base *ab);
+int ath12k_wmi_pdev_multiple_vdev_restart(struct ath12k *ar,
+ struct wmi_pdev_multiple_vdev_restart_req_arg *arg);
#endif