GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / net / wireless / realtek / rtw89 / ps.c
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
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
32 static void rtw89_ps_power_mode_change_with_hci(struct rtw89_dev *rtwdev,
33                                                 bool enter)
34 {
35         ieee80211_stop_queues(rtwdev->hw);
36         rtwdev->hci.paused = true;
37         flush_work(&rtwdev->txq_work);
38         ieee80211_wake_queues(rtwdev->hw);
39
40         rtw89_hci_pause(rtwdev, true);
41         rtw89_mac_power_mode_change(rtwdev, enter);
42         rtw89_hci_switch_mode(rtwdev, enter);
43         rtw89_hci_pause(rtwdev, false);
44
45         rtwdev->hci.paused = false;
46
47         if (!enter) {
48                 local_bh_disable();
49                 napi_schedule(&rtwdev->napi);
50                 local_bh_enable();
51         }
52 }
53
54 static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
55 {
56         if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode))
57                 rtw89_ps_power_mode_change_with_hci(rtwdev, enter);
58         else
59                 rtw89_mac_power_mode_change(rtwdev, enter);
60 }
61
62 static void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev)
63 {
64         if (!rtwdev->ps_mode)
65                 return;
66
67         if (test_and_set_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
68                 return;
69
70         rtw89_ps_power_mode_change(rtwdev, true);
71 }
72
73 void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
74 {
75         if (!rtwdev->ps_mode)
76                 return;
77
78         if (test_and_clear_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
79                 rtw89_ps_power_mode_change(rtwdev, false);
80 }
81
82 static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
83 {
84         struct rtw89_lps_parm lps_param = {
85                 .macid = mac_id,
86                 .psmode = RTW89_MAC_AX_PS_MODE_LEGACY,
87                 .lastrpwm = RTW89_LAST_RPWM_PS,
88         };
89
90         rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL);
91         rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
92 }
93
94 static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id)
95 {
96         struct rtw89_lps_parm lps_param = {
97                 .macid = mac_id,
98                 .psmode = RTW89_MAC_AX_PS_MODE_ACTIVE,
99                 .lastrpwm = RTW89_LAST_RPWM_ACTIVE,
100         };
101
102         rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
103         rtw89_fw_leave_lps_check(rtwdev, 0);
104         rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
105 }
106
107 void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
108 {
109         lockdep_assert_held(&rtwdev->mutex);
110
111         __rtw89_leave_ps_mode(rtwdev);
112 }
113
114 void rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
115 {
116         lockdep_assert_held(&rtwdev->mutex);
117
118         if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
119                 return;
120
121         __rtw89_enter_lps(rtwdev, mac_id);
122         __rtw89_enter_ps_mode(rtwdev);
123 }
124
125 static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
126 {
127         if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
128                 return;
129
130         __rtw89_leave_ps_mode(rtwdev);
131         __rtw89_leave_lps(rtwdev, rtwvif->mac_id);
132 }
133
134 void rtw89_leave_lps(struct rtw89_dev *rtwdev)
135 {
136         struct rtw89_vif *rtwvif;
137
138         lockdep_assert_held(&rtwdev->mutex);
139
140         if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
141                 return;
142
143         rtw89_for_each_rtwvif(rtwdev, rtwvif)
144                 rtw89_leave_lps_vif(rtwdev, rtwvif);
145 }
146
147 void rtw89_enter_ips(struct rtw89_dev *rtwdev)
148 {
149         struct rtw89_vif *rtwvif;
150
151         set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
152
153         rtw89_for_each_rtwvif(rtwdev, rtwvif)
154                 rtw89_mac_vif_deinit(rtwdev, rtwvif);
155
156         rtw89_core_stop(rtwdev);
157 }
158
159 void rtw89_leave_ips(struct rtw89_dev *rtwdev)
160 {
161         struct rtw89_vif *rtwvif;
162         int ret;
163
164         ret = rtw89_core_start(rtwdev);
165         if (ret)
166                 rtw89_err(rtwdev, "failed to leave idle state\n");
167
168         rtw89_set_channel(rtwdev);
169
170         rtw89_for_each_rtwvif(rtwdev, rtwvif)
171                 rtw89_mac_vif_init(rtwdev, rtwvif);
172
173         clear_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
174 }
175
176 void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl)
177 {
178         if (btc_ctrl)
179                 rtw89_leave_lps(rtwdev);
180 }