xref: /linux/drivers/net/wireless/realtek/rtw88/ps.c (revision 74acee309fb2a434dce215d44014e6f8e06975ae)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright(c) 2018-2019  Realtek Corporation
3  */
4 
5 #include "main.h"
6 #include "fw.h"
7 #include "ps.h"
8 #include "mac.h"
9 #include "debug.h"
10 
11 static int rtw_ips_pwr_up(struct rtw_dev *rtwdev)
12 {
13 	int ret;
14 
15 	ret = rtw_core_start(rtwdev);
16 	if (ret)
17 		rtw_err(rtwdev, "leave idle state failed\n");
18 
19 	rtw_set_channel(rtwdev);
20 	rtw_flag_clear(rtwdev, RTW_FLAG_INACTIVE_PS);
21 
22 	return ret;
23 }
24 
25 int rtw_enter_ips(struct rtw_dev *rtwdev)
26 {
27 	rtw_flag_set(rtwdev, RTW_FLAG_INACTIVE_PS);
28 
29 	rtw_core_stop(rtwdev);
30 
31 	return 0;
32 }
33 
34 static void rtw_restore_port_cfg_iter(void *data, u8 *mac,
35 				      struct ieee80211_vif *vif)
36 {
37 	struct rtw_dev *rtwdev = data;
38 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
39 	u32 config = ~0;
40 
41 	rtw_vif_port_config(rtwdev, rtwvif, config);
42 }
43 
44 int rtw_leave_ips(struct rtw_dev *rtwdev)
45 {
46 	int ret;
47 
48 	ret = rtw_ips_pwr_up(rtwdev);
49 	if (ret) {
50 		rtw_err(rtwdev, "failed to leave ips state\n");
51 		return ret;
52 	}
53 
54 	rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev);
55 
56 	return 0;
57 }
58 
59 static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
60 {
61 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
62 
63 	conf->state = RTW_ALL_ON;
64 	conf->awake_interval = 1;
65 	conf->rlbm = 0;
66 	conf->smart_ps = 0;
67 
68 	rtw_fw_set_pwr_mode(rtwdev);
69 	rtw_flag_clear(rtwdev, RTW_FLAG_LEISURE_PS);
70 }
71 
72 static void rtw_enter_lps_core(struct rtw_dev *rtwdev)
73 {
74 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
75 
76 	conf->state = RTW_RF_OFF;
77 	conf->awake_interval = 1;
78 	conf->rlbm = 1;
79 	conf->smart_ps = 2;
80 
81 	rtw_fw_set_pwr_mode(rtwdev);
82 	rtw_flag_set(rtwdev, RTW_FLAG_LEISURE_PS);
83 }
84 
85 void rtw_lps_work(struct work_struct *work)
86 {
87 	struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
88 					      lps_work.work);
89 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
90 	struct rtw_vif *rtwvif = conf->rtwvif;
91 
92 	if (WARN_ON(!rtwvif))
93 		return;
94 
95 	if (conf->mode == RTW_MODE_LPS)
96 		rtw_enter_lps_core(rtwdev);
97 	else
98 		rtw_leave_lps_core(rtwdev);
99 }
100 
101 void rtw_enter_lps_irqsafe(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif)
102 {
103 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
104 
105 	if (rtwvif->in_lps)
106 		return;
107 
108 	conf->mode = RTW_MODE_LPS;
109 	conf->rtwvif = rtwvif;
110 	rtwvif->in_lps = true;
111 
112 	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->lps_work, 0);
113 }
114 
115 void rtw_leave_lps_irqsafe(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif)
116 {
117 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
118 
119 	if (!rtwvif->in_lps)
120 		return;
121 
122 	conf->mode = RTW_MODE_ACTIVE;
123 	conf->rtwvif = rtwvif;
124 	rtwvif->in_lps = false;
125 
126 	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->lps_work, 0);
127 }
128 
129 bool rtw_in_lps(struct rtw_dev *rtwdev)
130 {
131 	return rtw_flag_check(rtwdev, RTW_FLAG_LEISURE_PS);
132 }
133 
134 void rtw_enter_lps(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif)
135 {
136 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
137 
138 	if (WARN_ON(!rtwvif))
139 		return;
140 
141 	if (rtwvif->in_lps)
142 		return;
143 
144 	conf->mode = RTW_MODE_LPS;
145 	conf->rtwvif = rtwvif;
146 	rtwvif->in_lps = true;
147 
148 	rtw_enter_lps_core(rtwdev);
149 }
150 
151 void rtw_leave_lps(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif)
152 {
153 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
154 
155 	if (WARN_ON(!rtwvif))
156 		return;
157 
158 	if (!rtwvif->in_lps)
159 		return;
160 
161 	conf->mode = RTW_MODE_ACTIVE;
162 	conf->rtwvif = rtwvif;
163 	rtwvif->in_lps = false;
164 
165 	rtw_leave_lps_core(rtwdev);
166 }
167