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