static int ath12k_start_vdev_delay(struct ath12k *ar,
                                   struct ath12k_vif *arvif);
 static void ath12k_mac_stop(struct ath12k *ar);
+static int ath12k_mac_vdev_create(struct ath12k *ar, struct ieee80211_vif *vif);
+static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ieee80211_vif *vif);
 
 static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode)
 {
        mutex_unlock(&ar->conf_mutex);
 }
 
+static struct ath12k*
+ath12k_mac_select_scan_device(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_scan_request *req)
+{
+       struct ath12k_hw *ah = hw->priv;
+       enum nl80211_band band;
+       struct ath12k *ar;
+       int i;
+
+       if (ah->num_radio == 1)
+               return ah->radio;
+
+       /* Currently mac80211 supports splitting scan requests into
+        * multiple scan requests per band.
+        * Loop through first channel and determine the scan radio
+        * TODO: There could be 5 GHz low/high channels in that case
+        * split the hw request and perform multiple scans
+        */
+
+       if (req->req.channels[0]->center_freq < ATH12K_MIN_5G_FREQ)
+               band = NL80211_BAND_2GHZ;
+       else if (req->req.channels[0]->center_freq < ATH12K_MIN_6G_FREQ)
+               band = NL80211_BAND_5GHZ;
+       else
+               band = NL80211_BAND_6GHZ;
+
+       for_each_ar(ah, ar, i) {
+               /* TODO 5 GHz low high split changes */
+               if (ar->mac.sbands[band].channels)
+                       return ar;
+       }
+
+       return NULL;
+}
+
 void __ath12k_mac_scan_finish(struct ath12k *ar)
 {
        struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
                                 struct ieee80211_scan_request *hw_req)
 {
        struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
-       struct ath12k *ar;
+       struct ath12k *ar, *prev_ar;
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        struct cfg80211_scan_request *req = &hw_req->req;
        struct ath12k_wmi_scan_req_arg arg = {};
        int ret;
        int i;
+       bool create = true;
 
-       ar = ath12k_ah_to_ar(ah, 0);
+       if (ah->num_radio == 1) {
+               WARN_ON(!arvif->is_created);
+               ar = ath12k_ah_to_ar(ah, 0);
+               goto scan;
+       }
+
+       /* Since the targeted scan device could depend on the frequency
+        * requested in the hw_req, select the corresponding radio
+        */
+       ar = ath12k_mac_select_scan_device(hw, vif, hw_req);
+       if (!ar)
+               return -EINVAL;
+
+       /* If the vif is already assigned to a specific vdev of an ar,
+        * check whether its already started, vdev which is started
+        * are not allowed to switch to a new radio.
+        * If the vdev is not started, but was earlier created on a
+        * different ar, delete that vdev and create a new one. We don't
+        * delete at the scan stop as an optimization to avoid redundant
+        * delete-create vdev's for the same ar, in case the request is
+        * always on the same band for the vif
+        */
+       if (arvif->is_created) {
+               if (WARN_ON(!arvif->ar))
+                       return -EINVAL;
+
+               if (ar != arvif->ar && arvif->is_started)
+                       return -EINVAL;
 
+               if (ar != arvif->ar) {
+                       /* backup the previously used ar ptr, since the vdev delete
+                        * would assign the arvif->ar to NULL after the call
+                        */
+                       prev_ar = arvif->ar;
+                       mutex_lock(&prev_ar->conf_mutex);
+                       ret = ath12k_mac_vdev_delete(prev_ar, vif);
+                       mutex_unlock(&prev_ar->conf_mutex);
+                       if (ret)
+                               ath12k_warn(prev_ar->ab,
+                                           "unable to delete scan vdev %d\n", ret);
+               } else {
+                       create = false;
+               }
+       }
+       if (create) {
+               mutex_lock(&ar->conf_mutex);
+               ret = ath12k_mac_vdev_create(ar, vif);
+               mutex_unlock(&ar->conf_mutex);
+               if (ret) {
+                       ath12k_warn(ar->ab, "unable to create scan vdev %d\n", ret);
+                       return -EINVAL;
+               }
+       }
+scan:
        mutex_lock(&ar->conf_mutex);
 
        spin_lock_bh(&ar->data_lock);
 static void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif)
 {
-       struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
+       struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        struct ath12k *ar;
 
-       ar = ath12k_ah_to_ar(ah, 0);
+       if (!arvif->is_created)
+               return;
+
+       ar = arvif->ar;
 
        mutex_lock(&ar->conf_mutex);
        ath12k_scan_abort(ar);
        if (vif->type != NL80211_IFTYPE_MONITOR && ar->monitor_conf_enabled)
                ath12k_mac_monitor_vdev_create(ar);
 
+       arvif->ar = ar;
        return ret;
 
 err_peer_del:
        ath12k_wmi_vdev_delete(ar, arvif->vdev_id);
        ar->num_created_vdevs--;
        arvif->is_created = false;
+       arvif->ar = NULL;
        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);
 {
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
        struct ath12k_hw *ah = hw->priv;
+       struct ath12k *ar, *prev_ar;
        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)
        if (!ar)
                return NULL;
 
+       if (arvif->ar) {
+               /* This is not expected really */
+               if (WARN_ON(!arvif->is_created)) {
+                       arvif->ar = NULL;
+                       return NULL;
+               }
+
+               if (ah->num_radio == 1)
+                       return arvif->ar;
+
+               /* This can happen as scan vdev gets created during multiple scans
+                * across different radios before a vdev is brought up in
+                * a certain radio.
+                */
+               if (ar != arvif->ar) {
+                       if (WARN_ON(arvif->is_started))
+                               return NULL;
+
+                       /* backup the previously used ar ptr since arvif->ar would
+                        * be set to NULL after vdev delete is done
+                        */
+                       prev_ar = arvif->ar;
+                       mutex_lock(&prev_ar->conf_mutex);
+                       ret = ath12k_mac_vdev_delete(prev_ar, vif);
+
+                       if (ret)
+                               ath12k_warn(prev_ar->ab, "unable to delete vdev %d\n",
+                                           ret);
+                       mutex_unlock(&prev_ar->conf_mutex);
+               }
+       }
+
        ab = ar->ab;
 
+       if (arvif->is_created)
+               goto out;
+
        mutex_lock(&ar->conf_mutex);
 
        if (vif->type == NL80211_IFTYPE_AP &&
        }
 }
 
-static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw,
-                                          struct ieee80211_vif *vif)
+static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ieee80211_vif *vif)
 {
-       struct ath12k *ar;
        struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
-       struct ath12k_base *ab;
+       struct ath12k_base *ab = ar->ab;
        unsigned long time_left;
        int ret;
 
-       if (!arvif->is_created)
-               return;
-
-       ar = arvif->ar;
-       ab = ar->ab;
-
-       mutex_lock(&ar->conf_mutex);
-
-       ath12k_dbg(ab, ATH12K_DBG_MAC, "mac remove interface (vdev %d)\n",
-                  arvif->vdev_id);
-
-       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
-               ret = ath12k_peer_delete(ar, arvif->vdev_id, vif->addr);
-               if (ret)
-                       ath12k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n",
-                                   arvif->vdev_id, ret);
-       }
-
+       lockdep_assert_held(&ar->conf_mutex);
        reinit_completion(&ar->vdev_delete_done);
 
        ret = ath12k_wmi_vdev_delete(ar, arvif->vdev_id);
                goto err_vdev_del;
        }
 
+       ab->free_vdev_map |= 1LL << arvif->vdev_id;
+       ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id);
+       ar->num_created_vdevs--;
+
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
                ar->monitor_vdev_id = -1;
                ar->monitor_vdev_created = false;
                ret = ath12k_mac_monitor_vdev_delete(ar);
        }
 
-       ab->free_vdev_map |= 1LL << (arvif->vdev_id);
-       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);
 
        clear_bit(ATH12K_FLAG_MONITOR_ENABLED, &ar->monitor_flags);
 
        /* TODO: recal traffic pause state based on the available vdevs */
+       arvif->is_created = false;
+       arvif->ar = NULL;
+
+       return ret;
+}
+
+static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif)
+{
+       struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
+       struct ath12k_base *ab;
+       struct ath12k *ar;
+       int ret;
+
+       if (!arvif->is_created)
+               return;
+
+       ar = arvif->ar;
+       ab = ar->ab;
+
+       mutex_lock(&ar->conf_mutex);
+
+       ath12k_dbg(ab, ATH12K_DBG_MAC, "mac remove interface (vdev %d)\n",
+                  arvif->vdev_id);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath12k_peer_delete(ar, arvif->vdev_id, vif->addr);
+               if (ret)
+                       ath12k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+       }
+
+       ath12k_mac_vdev_delete(ar, vif);
 
        mutex_unlock(&ar->conf_mutex);
 }