--- /dev/null
+From 3918dd0177ee08970683a2c22a3388825d82fd79 Mon Sep 17 00:00:00 2001
+From: Ping-Ke Shih <pkshih@realtek.com>
+Date: Sat, 27 May 2023 16:29:37 +0800
+Subject: wifi: rtw88: correct PS calculation for SUPPORTS_DYNAMIC_PS
+
+From: Ping-Ke Shih <pkshih@realtek.com>
+
+commit 3918dd0177ee08970683a2c22a3388825d82fd79 upstream.
+
+This driver relies on IEEE80211_CONF_PS of hw->conf.flags to turn off PS or
+turn on dynamic PS controlled by driver and firmware. Though this would be
+incorrect, it did work before because the flag is always recalculated until
+the commit 28977e790b5d ("wifi: mac80211: skip powersave recalc if driver SUPPORTS_DYNAMIC_PS")
+is introduced by kernel 5.20 to skip to recalculate IEEE80211_CONF_PS
+of hw->conf.flags if driver sets SUPPORTS_DYNAMIC_PS.
+
+Correct this by doing recalculation while BSS_CHANGED_PS is changed and
+interface is added or removed. It is allowed to enter PS only if single
+one station vif is working. Without this fix, driver doesn't enter PS
+anymore that causes higher power consumption.
+
+Fixes: bcde60e599fb ("rtw88: remove misleading module parameter rtw_fw_support_lps")
+Cc: stable@vger.kernel.org # 6.1+
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/20230527082939.11206-2-pkshih@realtek.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtw88/mac80211.c | 14 +++-----
+ drivers/net/wireless/realtek/rtw88/main.c | 4 +-
+ drivers/net/wireless/realtek/rtw88/ps.c | 43 ++++++++++++++++++++++++++
+ drivers/net/wireless/realtek/rtw88/ps.h | 2 +
+ 4 files changed, 52 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
+@@ -88,15 +88,6 @@ static int rtw_ops_config(struct ieee802
+ }
+ }
+
+- if (changed & IEEE80211_CONF_CHANGE_PS) {
+- if (hw->conf.flags & IEEE80211_CONF_PS) {
+- rtwdev->ps_enabled = true;
+- } else {
+- rtwdev->ps_enabled = false;
+- rtw_leave_lps(rtwdev);
+- }
+- }
+-
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+ rtw_set_channel(rtwdev);
+
+@@ -206,6 +197,7 @@ static int rtw_ops_add_interface(struct
+ rtwvif->bcn_ctrl = bcn_ctrl;
+ config |= PORT_SET_BCN_CTRL;
+ rtw_vif_port_config(rtwdev, rtwvif, config);
++ rtw_recalc_lps(rtwdev, vif);
+
+ mutex_unlock(&rtwdev->mutex);
+
+@@ -236,6 +228,7 @@ static void rtw_ops_remove_interface(str
+ rtwvif->bcn_ctrl = 0;
+ config |= PORT_SET_BCN_CTRL;
+ rtw_vif_port_config(rtwdev, rtwvif, config);
++ rtw_recalc_lps(rtwdev, NULL);
+
+ mutex_unlock(&rtwdev->mutex);
+ }
+@@ -428,6 +421,9 @@ static void rtw_ops_bss_info_changed(str
+ if (changed & BSS_CHANGED_ERP_SLOT)
+ rtw_conf_tx(rtwdev, rtwvif);
+
++ if (changed & BSS_CHANGED_PS)
++ rtw_recalc_lps(rtwdev, NULL);
++
+ rtw_vif_port_config(rtwdev, rtwvif, config);
+
+ mutex_unlock(&rtwdev->mutex);
+--- a/drivers/net/wireless/realtek/rtw88/main.c
++++ b/drivers/net/wireless/realtek/rtw88/main.c
+@@ -250,8 +250,8 @@ static void rtw_watch_dog_work(struct wo
+ * more than two stations associated to the AP, then we can not enter
+ * lps, because fw does not handle the overlapped beacon interval
+ *
+- * mac80211 should iterate vifs and determine if driver can enter
+- * ps by passing IEEE80211_CONF_PS to us, all we need to do is to
++ * rtw_recalc_lps() iterate vifs and determine if driver can enter
++ * ps by vif->type and vif->cfg.ps, all we need to do here is to
+ * get that vif and check if device is having traffic more than the
+ * threshold.
+ */
+--- a/drivers/net/wireless/realtek/rtw88/ps.c
++++ b/drivers/net/wireless/realtek/rtw88/ps.c
+@@ -299,3 +299,46 @@ void rtw_leave_lps_deep(struct rtw_dev *
+
+ __rtw_leave_lps_deep(rtwdev);
+ }
++
++struct rtw_vif_recalc_lps_iter_data {
++ struct rtw_dev *rtwdev;
++ struct ieee80211_vif *found_vif;
++ int count;
++};
++
++static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data,
++ struct ieee80211_vif *vif)
++{
++ if (data->count < 0)
++ return;
++
++ if (vif->type != NL80211_IFTYPE_STATION) {
++ data->count = -1;
++ return;
++ }
++
++ data->count++;
++ data->found_vif = vif;
++}
++
++static void rtw_vif_recalc_lps_iter(void *data, u8 *mac,
++ struct ieee80211_vif *vif)
++{
++ __rtw_vif_recalc_lps(data, vif);
++}
++
++void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif)
++{
++ struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev };
++
++ if (new_vif)
++ __rtw_vif_recalc_lps(&data, new_vif);
++ rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data);
++
++ if (data.count == 1 && data.found_vif->cfg.ps) {
++ rtwdev->ps_enabled = true;
++ } else {
++ rtwdev->ps_enabled = false;
++ rtw_leave_lps(rtwdev);
++ }
++}
+--- a/drivers/net/wireless/realtek/rtw88/ps.h
++++ b/drivers/net/wireless/realtek/rtw88/ps.h
+@@ -23,4 +23,6 @@ void rtw_enter_lps(struct rtw_dev *rtwde
+ void rtw_leave_lps(struct rtw_dev *rtwdev);
+ void rtw_leave_lps_deep(struct rtw_dev *rtwdev);
+ enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev);
++void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif);
++
+ #endif
--- /dev/null
+From 26a125f550a3bf86ac91d38752f4d446426dfe1c Mon Sep 17 00:00:00 2001
+From: Ping-Ke Shih <pkshih@realtek.com>
+Date: Sat, 27 May 2023 16:29:38 +0800
+Subject: wifi: rtw89: correct PS calculation for SUPPORTS_DYNAMIC_PS
+
+From: Ping-Ke Shih <pkshih@realtek.com>
+
+commit 26a125f550a3bf86ac91d38752f4d446426dfe1c upstream.
+
+This driver relies on IEEE80211_CONF_PS of hw->conf.flags to turn off PS or
+turn on dynamic PS controlled by driver and firmware. Though this would be
+incorrect, it did work before because the flag is always recalculated until
+the commit 28977e790b5d ("wifi: mac80211: skip powersave recalc if driver SUPPORTS_DYNAMIC_PS")
+is introduced by kernel 5.20 to skip to recalculate IEEE80211_CONF_PS
+of hw->conf.flags if driver sets SUPPORTS_DYNAMIC_PS.
+
+Correct this by doing recalculation while BSS_CHANGED_PS is changed and
+interface is added or removed. For now, it is allowed to enter PS only if
+single one station vif is working, and it could possible to have PS per
+vif after firmware can support it. Without this fix, driver doesn't
+enter PS anymore that causes higher power consumption.
+
+Fixes: e3ec7017f6a2 ("rtw89: add Realtek 802.11ax driver")
+Cc: stable@vger.kernel.org # 6.1+
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/20230527082939.11206-3-pkshih@realtek.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtw89/mac80211.c | 16 +++++++---------
+ drivers/net/wireless/realtek/rtw89/ps.c | 26 ++++++++++++++++++++++++++
+ drivers/net/wireless/realtek/rtw89/ps.h | 1 +
+ 3 files changed, 34 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
+@@ -79,15 +79,6 @@ static int rtw89_ops_config(struct ieee8
+ !(hw->conf.flags & IEEE80211_CONF_IDLE))
+ rtw89_leave_ips(rtwdev);
+
+- if (changed & IEEE80211_CONF_CHANGE_PS) {
+- if (hw->conf.flags & IEEE80211_CONF_PS) {
+- rtwdev->lps_enabled = true;
+- } else {
+- rtw89_leave_lps(rtwdev);
+- rtwdev->lps_enabled = false;
+- }
+- }
+-
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ rtw89_config_entity_chandef(rtwdev, RTW89_SUB_ENTITY_0,
+ &hw->conf.chandef);
+@@ -147,6 +138,8 @@ static int rtw89_ops_add_interface(struc
+ rtw89_core_txq_init(rtwdev, vif->txq);
+
+ rtw89_btc_ntfy_role_info(rtwdev, rtwvif, NULL, BTC_ROLE_START);
++
++ rtw89_recalc_lps(rtwdev);
+ out:
+ mutex_unlock(&rtwdev->mutex);
+
+@@ -170,6 +163,8 @@ static void rtw89_ops_remove_interface(s
+ rtw89_mac_remove_vif(rtwdev, rtwvif);
+ rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif->port);
+ list_del_init(&rtwvif->list);
++ rtw89_recalc_lps(rtwdev);
++
+ mutex_unlock(&rtwdev->mutex);
+ }
+
+@@ -425,6 +420,9 @@ static void rtw89_ops_bss_info_changed(s
+ if (changed & BSS_CHANGED_P2P_PS)
+ rtw89_process_p2p_ps(rtwdev, vif);
+
++ if (changed & BSS_CHANGED_PS)
++ rtw89_recalc_lps(rtwdev);
++
+ mutex_unlock(&rtwdev->mutex);
+ }
+
+--- a/drivers/net/wireless/realtek/rtw89/ps.c
++++ b/drivers/net/wireless/realtek/rtw89/ps.c
+@@ -244,3 +244,29 @@ void rtw89_process_p2p_ps(struct rtw89_d
+ rtw89_p2p_disable_all_noa(rtwdev, vif);
+ rtw89_p2p_update_noa(rtwdev, vif);
+ }
++
++void rtw89_recalc_lps(struct rtw89_dev *rtwdev)
++{
++ struct ieee80211_vif *vif, *found_vif = NULL;
++ struct rtw89_vif *rtwvif;
++ int count = 0;
++
++ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
++ vif = rtwvif_to_vif(rtwvif);
++
++ if (vif->type != NL80211_IFTYPE_STATION) {
++ count = 0;
++ break;
++ }
++
++ count++;
++ found_vif = vif;
++ }
++
++ if (count == 1 && found_vif->cfg.ps) {
++ rtwdev->lps_enabled = true;
++ } else {
++ rtw89_leave_lps(rtwdev);
++ rtwdev->lps_enabled = false;
++ }
++}
+--- a/drivers/net/wireless/realtek/rtw89/ps.h
++++ b/drivers/net/wireless/realtek/rtw89/ps.h
+@@ -14,5 +14,6 @@ void rtw89_enter_ips(struct rtw89_dev *r
+ void rtw89_leave_ips(struct rtw89_dev *rtwdev);
+ void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl);
+ void rtw89_process_p2p_ps(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif);
++void rtw89_recalc_lps(struct rtw89_dev *rtwdev);
+
+ #endif