1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright(c) 2019-2020 Realtek Corporation
3 */
4
5 #include "coex.h"
6 #include "core.h"
7 #include "debug.h"
8 #include "fw.h"
9 #include "mac.h"
10 #include "ps.h"
11 #include "reg.h"
12 #include "util.h"
13
rtw89_fw_leave_lps_check(struct rtw89_dev * rtwdev,u8 macid)14 static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid)
15 {
16 u32 pwr_en_bit = 0xE;
17 u32 chk_msk = pwr_en_bit << (4 * macid);
18 u32 polling;
19 int ret;
20
21 ret = read_poll_timeout_atomic(rtw89_read32_mask, polling, !polling,
22 1000, 50000, false, rtwdev,
23 R_AX_PPWRBIT_SETTING, chk_msk);
24 if (ret) {
25 rtw89_info(rtwdev, "rtw89: failed to leave lps state\n");
26 return -EBUSY;
27 }
28
29 return 0;
30 }
31
__rtw89_enter_ps_mode(struct rtw89_dev * rtwdev)32 static void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev)
33 {
34 if (!rtwdev->ps_mode)
35 return;
36
37 if (test_and_set_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
38 return;
39
40 rtw89_mac_power_mode_change(rtwdev, true);
41 }
42
__rtw89_leave_ps_mode(struct rtw89_dev * rtwdev)43 void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
44 {
45 if (!rtwdev->ps_mode)
46 return;
47
48 if (test_and_clear_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
49 rtw89_mac_power_mode_change(rtwdev, false);
50 }
51
__rtw89_enter_lps(struct rtw89_dev * rtwdev,u8 mac_id)52 static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
53 {
54 struct rtw89_lps_parm lps_param = {
55 .macid = mac_id,
56 .psmode = RTW89_MAC_AX_PS_MODE_LEGACY,
57 .lastrpwm = RTW89_LAST_RPWM_PS,
58 };
59
60 rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL);
61 rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
62 }
63
__rtw89_leave_lps(struct rtw89_dev * rtwdev,u8 mac_id)64 static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id)
65 {
66 struct rtw89_lps_parm lps_param = {
67 .macid = mac_id,
68 .psmode = RTW89_MAC_AX_PS_MODE_ACTIVE,
69 .lastrpwm = RTW89_LAST_RPWM_ACTIVE,
70 };
71
72 rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
73 rtw89_fw_leave_lps_check(rtwdev, 0);
74 rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
75 }
76
rtw89_leave_ps_mode(struct rtw89_dev * rtwdev)77 void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
78 {
79 lockdep_assert_held(&rtwdev->mutex);
80
81 __rtw89_leave_ps_mode(rtwdev);
82 }
83
rtw89_enter_lps(struct rtw89_dev * rtwdev,u8 mac_id)84 void rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
85 {
86 lockdep_assert_held(&rtwdev->mutex);
87
88 if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
89 return;
90
91 __rtw89_enter_lps(rtwdev, mac_id);
92 __rtw89_enter_ps_mode(rtwdev);
93 }
94
rtw89_leave_lps_vif(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)95 static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
96 {
97 if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
98 return;
99
100 __rtw89_leave_ps_mode(rtwdev);
101 __rtw89_leave_lps(rtwdev, rtwvif->mac_id);
102 }
103
rtw89_leave_lps(struct rtw89_dev * rtwdev)104 void rtw89_leave_lps(struct rtw89_dev *rtwdev)
105 {
106 struct rtw89_vif *rtwvif;
107
108 lockdep_assert_held(&rtwdev->mutex);
109
110 if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
111 return;
112
113 rtw89_for_each_rtwvif(rtwdev, rtwvif)
114 rtw89_leave_lps_vif(rtwdev, rtwvif);
115 }
116
rtw89_enter_ips(struct rtw89_dev * rtwdev)117 void rtw89_enter_ips(struct rtw89_dev *rtwdev)
118 {
119 struct rtw89_vif *rtwvif;
120
121 set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
122
123 rtw89_for_each_rtwvif(rtwdev, rtwvif)
124 rtw89_mac_vif_deinit(rtwdev, rtwvif);
125
126 rtw89_core_stop(rtwdev);
127 }
128
rtw89_leave_ips(struct rtw89_dev * rtwdev)129 void rtw89_leave_ips(struct rtw89_dev *rtwdev)
130 {
131 struct rtw89_vif *rtwvif;
132 int ret;
133
134 ret = rtw89_core_start(rtwdev);
135 if (ret)
136 rtw89_err(rtwdev, "failed to leave idle state\n");
137
138 rtw89_set_channel(rtwdev);
139
140 rtw89_for_each_rtwvif(rtwdev, rtwvif)
141 rtw89_mac_vif_init(rtwdev, rtwvif);
142
143 clear_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
144 }
145
rtw89_set_coex_ctrl_lps(struct rtw89_dev * rtwdev,bool btc_ctrl)146 void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl)
147 {
148 if (btc_ctrl)
149 rtw89_leave_lps(rtwdev);
150 }
151