ath12k_mac_update_vif_offload(arvif);
 }
 
-static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
-                                      struct ieee80211_vif *vif)
+static int ath12k_mac_vdev_create(struct ath12k *ar, struct ieee80211_vif *vif)
 {
-       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
-       struct ath12k *ar;
-       struct ath12k_base *ab;
+       struct ath12k_hw *ah = ar->ah;
+       struct ath12k_base *ab = ar->ab;
+       struct ieee80211_hw *hw = ah->hw;
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        struct ath12k_wmi_vdev_create_arg vdev_arg = {0};
        struct ath12k_wmi_peer_create_arg peer_param;
        u32 param_id, param_value;
        u16 nss;
        int i;
-       int ret;
-       int bit;
-
-       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
-
-       ar = ath12k_ah_to_ar(ah, 0);
-       ab = ar->ab;
-
-       mutex_lock(&ar->conf_mutex);
-
-       if (vif->type == NL80211_IFTYPE_AP &&
-           ar->num_peers > (ar->max_num_peers - 1)) {
-               ath12k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n");
-               ret = -ENOBUFS;
-               goto err;
-       }
-
-       if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) {
-               ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
-                           TARGET_NUM_VDEVS);
-               ret = -EBUSY;
-               goto err;
-       }
+       int ret, vdev_id;
 
-       memset(arvif, 0, sizeof(*arvif));
+       lockdep_assert_held(&ar->conf_mutex);
 
        arvif->ar = ar;
-       arvif->vif = vif;
-
-       INIT_LIST_HEAD(&arvif->list);
-
-       /* Should we initialize any worker to handle connection loss indication
-        * from firmware in sta mode?
-        */
-
-       for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
-               arvif->bitrate_mask.control[i].legacy = 0xffffffff;
-               memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff,
-                      sizeof(arvif->bitrate_mask.control[i].ht_mcs));
-               memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff,
-                      sizeof(arvif->bitrate_mask.control[i].vht_mcs));
-       }
-
-       bit = __ffs64(ab->free_vdev_map);
-
-       arvif->vdev_id = bit;
+       vdev_id = __ffs64(ab->free_vdev_map);
+       arvif->vdev_id = vdev_id;
        arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
 
        switch (vif->type) {
                break;
        case NL80211_IFTYPE_MONITOR:
                arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
-               ar->monitor_vdev_id = bit;
+               ar->monitor_vdev_id = vdev_id;
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                arvif->vdev_type = WMI_VDEV_TYPE_STA;
                break;
        }
 
-       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac add interface id %d type %d subtype %d map %llx\n",
+       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev create id %d type %d subtype %d map %llx\n",
                   arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype,
                   ab->free_vdev_map);
 
        }
 
        ar->num_created_vdevs++;
+       arvif->is_created = true;
        ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM created, vdev_id %d\n",
                   vif->addr, arvif->vdev_id);
        ar->allocated_vdev_map |= 1LL << arvif->vdev_id;
        if (vif->type != NL80211_IFTYPE_MONITOR && ar->monitor_conf_enabled)
                ath12k_mac_monitor_vdev_create(ar);
 
-       mutex_unlock(&ar->conf_mutex);
-
        return ret;
 
 err_peer_del:
 err_vdev_del:
        ath12k_wmi_vdev_delete(ar, arvif->vdev_id);
        ar->num_created_vdevs--;
+       arvif->is_created = false;
        ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
        ab->free_vdev_map |= 1LL << arvif->vdev_id;
        ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id);
        spin_unlock_bh(&ar->data_lock);
 
 err:
+       arvif->ar = NULL;
+       return ret;
+}
+
+static struct ath12k *ath12k_mac_assign_vif_to_vdev(struct ieee80211_hw *hw,
+                                                   struct ieee80211_vif *vif,
+                                                   struct ieee80211_chanctx_conf *ctx)
+{
+       struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
+       struct ath12k_hw *ah = hw->priv;
+       struct ath12k_base *ab;
+       struct ath12k *ar;
+       int ret;
+
+       if (arvif->ar) {
+               WARN_ON(!arvif->is_created);
+               goto out;
+       }
+
+       if (ah->num_radio == 1)
+               ar = ah->radio;
+       else if (ctx)
+               ar = ath12k_get_ar_by_ctx(hw, ctx);
+       else
+               return NULL;
+
+       if (!ar)
+               return NULL;
+
+       ab = ar->ab;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (vif->type == NL80211_IFTYPE_AP &&
+           ar->num_peers > (ar->max_num_peers - 1)) {
+               ath12k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n");
+               goto unlock;
+       }
+
+       if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) {
+               ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
+                           TARGET_NUM_VDEVS);
+               goto unlock;
+       }
+
+       ret = ath12k_mac_vdev_create(ar, vif);
+       if (ret) {
+               ath12k_warn(ab, "failed to create vdev %pM ret %d", vif->addr, ret);
+               goto unlock;
+       }
+
+       /* TODO If the vdev is created during channel assign and not during
+        * add_interface(), Apply any parameters for the vdev which were received
+        * after add_interface, corresponding to this vif.
+        */
+unlock:
        mutex_unlock(&ar->conf_mutex);
+out:
+       return arvif->ar;
+}
 
-       return ret;
+static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
+{
+       struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
+       int i;
+
+       memset(arvif, 0, sizeof(*arvif));
+
+       arvif->vif = vif;
+
+       INIT_LIST_HEAD(&arvif->list);
+
+       for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
+               arvif->bitrate_mask.control[i].legacy = 0xffffffff;
+               memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff,
+                      sizeof(arvif->bitrate_mask.control[i].ht_mcs));
+               memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff,
+                      sizeof(arvif->bitrate_mask.control[i].vht_mcs));
+       }
+
+       /* Allocate Default Queue now and reassign during actual vdev create */
+       vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE;
+       for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++)
+               vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE;
+
+       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+
+       /* For single radio wiphy(i.e ah->num_radio is 1), create the vdev
+        * during add_interface itself, for multi radio wiphy, defer the vdev
+        * creation until channel_assign to determine the radio on which the
+        * vdev needs to be created
+        */
+       ath12k_mac_assign_vif_to_vdev(hw, vif, NULL);
+       return 0;
 }
 
 static void ath12k_mac_vif_unref(struct ath12k_dp *dp, struct ieee80211_vif *vif)
 static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw,
                                           struct ieee80211_vif *vif)
 {
-       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
        struct ath12k *ar;
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        struct ath12k_base *ab;
        unsigned long time_left;
        int ret;
 
-       ar = ath12k_ah_to_ar(ah, 0);
+       if (!arvif->is_created)
+               return;
+
+       ar = arvif->ar;
        ab = ar->ab;
 
        mutex_lock(&ar->conf_mutex);
        ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
        ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id);
        ar->num_created_vdevs--;
+       arvif->is_created = false;
 
        ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n",
                   vif->addr, arvif->vdev_id);
                                 struct ieee80211_bss_conf *link_conf,
                                 struct ieee80211_chanctx_conf *ctx)
 {
-       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
        struct ath12k *ar;
        struct ath12k_base *ab;
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        int ret;
        struct ath12k_wmi_peer_create_arg param;
 
-       ar = ath12k_ah_to_ar(ah, 0);
+       /* For multi radio wiphy, the vdev was not created during add_interface
+        * create now since we have a channel ctx now to assign to a specific ar/fw
+        */
+       ar = ath12k_mac_assign_vif_to_vdev(hw, vif, ctx);
+       if (!ar) {
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
        ab = ar->ab;
 
        mutex_lock(&ar->conf_mutex);
                                   struct ieee80211_bss_conf *link_conf,
                                   struct ieee80211_chanctx_conf *ctx)
 {
-       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
        struct ath12k *ar;
        struct ath12k_base *ab;
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        int ret;
 
-       ar = ath12k_ah_to_ar(ah, 0);
+       /* The vif is expected to be attached to an ar's VDEV.
+        * We leave the vif/vdev in this function as is
+        * and not delete the vdev symmetric to assign_vif_chanctx()
+        * the VDEV will be deleted and unassigned either during
+        * remove_interface() or when there is a change in channel
+        * that moves the vif to a new ar
+        */
+       if (!arvif->is_created)
+               return;
+
+       ar = arvif->ar;
        ab = ar->ab;
 
        mutex_lock(&ar->conf_mutex);
                                 int n_vifs,
                                 enum ieee80211_chanctx_switch_mode mode)
 {
-       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
        struct ath12k *ar;
 
-       ar = ath12k_ah_to_ar(ah, 0);
+       ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx);
+       if (!ar)
+               return -EINVAL;
 
        mutex_lock(&ar->conf_mutex);
 
+       /* Switching channels across radio is not allowed */
+       if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx)) {
+               mutex_unlock(&ar->conf_mutex);
+               return -EINVAL;
+       }
+
        ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
                   "mac chanctx switch n_vifs %d mode %d\n",
                   n_vifs, mode);