#define IWL_MVM_AUTO_EML_ENABLE                 true
 #define IWL_MVM_MISSED_BEACONS_EXIT_ESR_THRESH 7
 
+#define IWL_MVM_HIGH_RSSI_THRESH_20MHZ         -67
+#define IWL_MVM_LOW_RSSI_THRESH_20MHZ          -71
+#define IWL_MVM_HIGH_RSSI_THRESH_40MHZ         -64
+#define IWL_MVM_LOW_RSSI_THRESH_40MHZ          -67
+#define IWL_MVM_HIGH_RSSI_THRESH_80MHZ         -61
+#define IWL_MVM_LOW_RSSI_THRESH_80MHZ          -74
+#define IWL_MVM_HIGH_RSSI_THRESH_160MHZ                -58
+#define IWL_MVM_LOW_RSSI_THRESH_160MHZ         -61
+
 #endif /* __MVM_CONSTANTS_H */
 
                        continue;
 
                data[n_data].link_id = link_id;
-               data[n_data].band = link_conf->chanreq.oper.chan->band;
+               data[n_data].chandef = &link_conf->chanreq.oper;
+               data[n_data].signal = link_conf->bss->signal / 100;
                data[n_data].grade = iwl_mvm_get_link_grade(link_conf);
 
                if (data[n_data].grade > max_grade) {
        return n_data;
 }
 
+struct iwl_mvm_bw_to_rssi_threshs {
+       s8 low;
+       s8 high;
+};
+
+#define BW_TO_RSSI_THRESHOLDS(_bw)                             \
+       [IWL_PHY_CHANNEL_MODE ## _bw] = {                       \
+               .low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ,      \
+               .high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ     \
+       }
+
+s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
+                              const struct cfg80211_chan_def *chandef,
+                              bool low)
+{
+       const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map[] = {
+               BW_TO_RSSI_THRESHOLDS(20),
+               BW_TO_RSSI_THRESHOLDS(40),
+               BW_TO_RSSI_THRESHOLDS(80),
+               BW_TO_RSSI_THRESHOLDS(160)
+               /* 320 MHz has the same thresholds as 20 MHz */
+       };
+       const struct iwl_mvm_bw_to_rssi_threshs *threshs;
+       u8 chan_width = iwl_mvm_get_channel_width(chandef);
+
+       if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
+                   chandef->chan->band != NL80211_BAND_5GHZ &&
+                   chandef->chan->band != NL80211_BAND_6GHZ))
+               return S8_MAX;
+
+       /* 6 GHz will always use 20 MHz thresholds, regardless of the BW */
+       if (chan_width == IWL_PHY_CHANNEL_MODE320)
+               chan_width = IWL_PHY_CHANNEL_MODE20;
+
+       threshs = &bw_to_rssi_threshs_map[chan_width];
+
+       return low ? threshs->low : threshs->high;
+}
+
+static u32
+iwl_mvm_esr_disallowed_with_link(struct ieee80211_vif *vif,
+                                const struct iwl_mvm_link_sel_data *link)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       enum iwl_mvm_esr_state ret = 0;
+       s8 thresh;
+
+       /* BT Coex effects eSR mode only if one of the links is on LB */
+       if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
+           mvmvif->esr_disable_reason & IWL_MVM_ESR_BLOCKED_COEX)
+               ret |= IWL_MVM_ESR_BLOCKED_COEX;
+       thresh = iwl_mvm_get_esr_rssi_thresh(mvm, link->chandef,
+                                            false);
+
+       if (link->signal < thresh)
+               ret |= IWL_MVM_ESR_EXIT_LOW_RSSI;
+
+       if (ret)
+               IWL_DEBUG_INFO(mvm,
+                              "Link %d is not allowed for esr. Reason: 0x%x\n",
+                              link->link_id, ret);
+       return ret;
+}
+
 VISIBLE_IF_IWLWIFI_KUNIT
 bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
                                 const struct iwl_mvm_link_sel_data *a,
                                 const struct iwl_mvm_link_sel_data *b)
 {
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       if (a->band == b->band)
+       /* Per-link considerations */
+       if (iwl_mvm_esr_disallowed_with_link(vif, a) ||
+           iwl_mvm_esr_disallowed_with_link(vif, b))
                return false;
 
-       /* BT Coex effects eSR mode only if one of the link is on LB */
-       if (a->band == NL80211_BAND_2GHZ || b->band == NL80211_BAND_2GHZ)
-               return !(mvmvif->esr_disable_reason & IWL_MVM_ESR_BLOCKED_COEX);
-
-       return true;
+       /* Per-combination considerations */
+       return a->chandef->chan->band != b->chandef->chan->band;
 }
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair);
 
 
  *     in a loop.
  * @IWL_MVM_ESR_BLOCKED_WOWLAN: WOWLAN is preventing the enablement of EMLSR
  * @IWL_MVM_ESR_EXIT_MISSED_BEACON: exited EMLSR due to missed beacons
+ * @IWL_MVM_ESR_EXIT_LOW_RSSI: link is deactivated/not allowed for EMLSR
+ *     due to low RSSI.
  */
 enum iwl_mvm_esr_state {
        IWL_MVM_ESR_BLOCKED_COEX        = 0x1,
        IWL_MVM_ESR_BLOCKED_PREVENTION  = 0x2,
        IWL_MVM_ESR_BLOCKED_WOWLAN      = 0x4,
        IWL_MVM_ESR_EXIT_MISSED_BEACON  = 0x10000,
+       IWL_MVM_ESR_EXIT_LOW_RSSI       = 0x20000,
 };
 
 #define IWL_MVM_BLOCK_ESR_REASONS 0xffff
 
 struct iwl_mvm_link_sel_data {
        u8 link_id;
-       enum nl80211_band band;
+       const struct cfg80211_chan_def *chandef;
+       s32 signal;
        u16 grade;
 };
 
 void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                      enum iwl_mvm_esr_state reason,
                      u8 link_to_keep);
+s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
+                              const struct cfg80211_chan_def *chandef,
+                              bool low);
 
 #endif /* __IWL_MVM_H__ */
 
        struct iwl_stats_ntfy_per_mac *per_mac;
 };
 
-static void iwl_mvm_update_vif_sig(struct ieee80211_vif *vif, int sig,
-                                  struct iwl_mvm_vif_link_info *link_info)
+static void iwl_mvm_update_link_sig(struct ieee80211_vif *vif, int sig,
+                                   struct iwl_mvm_vif_link_info *link_info)
 {
        struct iwl_mvm *mvm = iwl_mvm_vif_from_mac80211(vif)->mvm;
        struct ieee80211_bss_conf *bss_conf =
        int thold = bss_conf->cqm_rssi_thold;
        int hyst = bss_conf->cqm_rssi_hyst;
        int last_event;
+       s8 exit_esr_thresh;
 
        if (sig == 0) {
                IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
                        sig,
                        GFP_KERNEL);
        }
+
+       /* ESR recalculation */
+       if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
+               return;
+
+       exit_esr_thresh =
+               iwl_mvm_get_esr_rssi_thresh(mvm,
+                                           &bss_conf->chanreq.oper,
+                                           true);
+
+       if (sig < exit_esr_thresh)
+               iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_LOW_RSSI,
+                                iwl_mvm_get_other_link(vif,
+                                                       bss_conf->link_id));
 }
 
 static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
                        mvmvif->deflink.beacon_stats.num_beacons;
 
        /* This is used in pre-MLO API so use deflink */
-       iwl_mvm_update_vif_sig(vif, sig, &mvmvif->deflink);
+       iwl_mvm_update_link_sig(vif, sig, &mvmvif->deflink);
 }
 
 static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac,
        sig = -le32_to_cpu(mac_stats->beacon_filter_average_energy);
 
        /* This is used in pre-MLO API so use deflink */
-       iwl_mvm_update_vif_sig(vif, sig, &mvmvif->deflink);
+       iwl_mvm_update_link_sig(vif, sig, &mvmvif->deflink);
 }
 
 static inline void
                                mvmvif->link[link_id]->beacon_stats.num_beacons;
 
                sig = -le32_to_cpu(link_stats->beacon_filter_average_energy);
-               iwl_mvm_update_vif_sig(bss_conf->vif, sig, link_info);
+               iwl_mvm_update_link_sig(bss_conf->vif, sig, link_info);
 
                if (WARN_ONCE(mvmvif->id >= MAC_INDEX_AUX,
                              "invalid mvmvif id: %d", mvmvif->id))
 
        .band = NL80211_BAND_2GHZ,
 };
 
+static struct cfg80211_chan_def chandef_a = {};
+
+static struct cfg80211_chan_def chandef_b = {};
+
 static struct iwl_mvm_phy_ctxt ctx = {};
 
 static struct iwl_mvm_vif_link_info mvm_link = {
 
 static struct ieee80211_bss_conf link_conf = {.bss = &bss};
 
+static struct iwl_mvm mvm = {};
+
 static const struct link_grading_case {
        const char *desc;
        const struct cfg80211_chan_def chandef;
 static const struct valid_link_pair_case {
        const char *desc;
        u32 esr_disable_reason;
-       enum nl80211_band band_a;
-       enum nl80211_band band_b;
+       struct ieee80211_channel *chan_a;
+       struct ieee80211_channel *chan_b;
+       enum nl80211_chan_width cw_a;
+       enum nl80211_chan_width cw_b;
+       s32 sig_a;
+       s32 sig_b;
        bool valid;
 } valid_link_pair_cases[] = {
        {
                .desc = "HB + UHB, valid.",
-               .band_a = NL80211_BAND_5GHZ,
-               .band_b = NL80211_BAND_6GHZ,
+               .chan_a = &chan_5ghz,
+               .chan_b = &chan_6ghz,
                .valid = true,
        },
        {
                .desc = "LB + HB, no BT.",
-               .band_a = NL80211_BAND_2GHZ,
-               .band_b = NL80211_BAND_5GHZ,
+               .chan_a = &chan_2ghz,
+               .chan_b = &chan_5ghz,
                .valid = true,
        },
        {
                .desc = "LB + HB, with BT.",
                .esr_disable_reason = 0x1,
-               .band_a = NL80211_BAND_2GHZ,
-               .band_b = NL80211_BAND_5GHZ,
+               .chan_a = &chan_2ghz,
+               .chan_b = &chan_5ghz,
                .valid = false,
        },
        {
                .desc = "Same band",
-               .band_a = NL80211_BAND_2GHZ,
-               .band_b = NL80211_BAND_2GHZ,
+               .chan_a = &chan_2ghz,
+               .chan_b = &chan_2ghz,
+               .valid = false,
+       },
+       {
+               .desc = "RSSI: LB, 20 MHz, low",
+               .chan_a = &chan_2ghz,
+               .cw_a = NL80211_CHAN_WIDTH_20,
+               .sig_a = -68,
+               .chan_b = &chan_5ghz,
+               .valid = false,
+       },
+       {
+               .desc = "RSSI: LB, 20 MHz, high",
+               .chan_a = &chan_2ghz,
+               .cw_a = NL80211_CHAN_WIDTH_20,
+               .sig_a = -66,
+               .chan_b = &chan_5ghz,
+               .valid = true,
+       },
+       {
+               .desc = "RSSI: LB, 40 MHz, low",
+               .chan_a = &chan_2ghz,
+               .cw_a = NL80211_CHAN_WIDTH_40,
+               .sig_a = -65,
+               .chan_b = &chan_5ghz,
+               .valid = false,
+       },
+       {
+               .desc = "RSSI: LB, 40 MHz, high",
+               .chan_a = &chan_2ghz,
+               .cw_a = NL80211_CHAN_WIDTH_40,
+               .sig_a = -63,
+               .chan_b = &chan_5ghz,
+               .valid = true,
+       },
+       {
+               .desc = "RSSI: HB, 80 MHz, low",
+               .chan_a = &chan_5ghz,
+               .cw_a = NL80211_CHAN_WIDTH_80,
+               .sig_a = -62,
+               .chan_b = &chan_2ghz,
                .valid = false,
        },
+       {
+               .desc = "RSSI: HB, 80 MHz, high",
+               .chan_a = &chan_5ghz,
+               .cw_a = NL80211_CHAN_WIDTH_80,
+               .sig_a = -60,
+               .chan_b = &chan_2ghz,
+               .valid = true,
+       },
+       {
+               .desc = "RSSI: HB, 160 MHz, low",
+               .chan_a = &chan_5ghz,
+               .cw_a = NL80211_CHAN_WIDTH_160,
+               .sig_a = -59,
+               .chan_b = &chan_2ghz,
+               .valid = false,
+       },
+       {
+               .desc = "RSSI: HB, 160 MHz, high",
+               .chan_a = &chan_5ghz,
+               .cw_a = NL80211_CHAN_WIDTH_160,
+               .sig_a = -5,
+               .chan_b = &chan_2ghz,
+               .valid = true,
+       },
+       {
+               .desc = "RSSI: UHB, 320 MHz, low",
+               .chan_a = &chan_6ghz,
+               .cw_a = NL80211_CHAN_WIDTH_320,
+               .sig_a = -68,
+               .chan_b = &chan_6ghz,
+               .valid = false,
+       },
+       {
+               .desc = "RSSI: UHB, 320 MHz, high",
+               .chan_a = &chan_6ghz,
+               .cw_a = NL80211_CHAN_WIDTH_320,
+               .sig_a = -66,
+               .chan_b = &chan_5ghz,
+               .valid = true,
+       },
 };
 
 KUNIT_ARRAY_PARAM_DESC(valid_link_pair, valid_link_pair_cases, desc)
        size_t vif_size = sizeof(struct ieee80211_vif) +
                sizeof(struct iwl_mvm_vif);
        struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL);
+       struct iwl_trans *trans = kunit_kzalloc(test, sizeof(struct iwl_trans),
+                                               GFP_KERNEL);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_link_sel_data link_a = {
-               .band = params->band_a,
+               .chandef = &chandef_a,
+               .link_id = 1,
+               .signal = params->sig_a,
        };
        struct iwl_mvm_link_sel_data link_b = {
-               .band = params->band_b,
+               .chandef = &chandef_b,
+               .link_id = 5,
+               .signal = params->sig_b,
        };
        bool result;
 
        KUNIT_ASSERT_NOT_NULL(test, vif);
+       KUNIT_ASSERT_NOT_NULL(test, trans);
+
+       chandef_a.chan = params->chan_a;
+       chandef_b.chan = params->chan_b;
+
+       chandef_a.width = params->cw_a ?: NL80211_CHAN_WIDTH_20;
+       chandef_b.width = params->cw_b ?: NL80211_CHAN_WIDTH_20;
+
+#ifdef CONFIG_IWLWIFI_SUPPORT_DEBUG_OVERRIDES
+       trans->dbg_cfg = default_dbg_config;
+#endif
+       mvm.trans = trans;
 
-       iwl_mvm_vif_from_mac80211(vif)->esr_disable_reason =
-               params->esr_disable_reason;
+       mvmvif->esr_disable_reason = params->esr_disable_reason;
+       mvmvif->mvm = &mvm;
 
        result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b);
 
        KUNIT_EXPECT_EQ(test, result, params->valid);
 
        kunit_kfree(test, vif);
+       kunit_kfree(test, trans);
 }
 
 static struct kunit_case valid_link_pair_test_cases[] = {