1 // SPDX-License-Identifier: GPL-2.0-only
3 * Mac80211 power management API for ST-Ericsson CW1200 drivers
5 * Copyright (c) 2011, ST-Ericsson
6 * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
9 #include <linux/module.h>
10 #include <linux/if_ether.h>
17 #define CW1200_BEACON_SKIPPING_MULTIPLIER 3
19 struct cw1200_udp_port_filter
{
20 struct wsm_udp_port_filter_hdr hdr
;
21 /* Up to 4 filters are allowed. */
22 struct wsm_udp_port_filter filters
[WSM_MAX_FILTER_ELEMENTS
];
25 struct cw1200_ether_type_filter
{
26 struct wsm_ether_type_filter_hdr hdr
;
27 /* Up to 4 filters are allowed. */
28 struct wsm_ether_type_filter filters
[WSM_MAX_FILTER_ELEMENTS
];
31 static struct cw1200_udp_port_filter cw1200_udp_port_filter_on
= {
35 .action
= WSM_FILTER_ACTION_FILTER_OUT
,
36 .type
= WSM_FILTER_PORT_TYPE_DST
,
37 .port
= __cpu_to_le16(67), /* DHCP Bootps */
40 .action
= WSM_FILTER_ACTION_FILTER_OUT
,
41 .type
= WSM_FILTER_PORT_TYPE_DST
,
42 .port
= __cpu_to_le16(68), /* DHCP Bootpc */
47 static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off
= {
52 #define ETH_P_WAPI 0x88B4
55 static struct cw1200_ether_type_filter cw1200_ether_type_filter_on
= {
59 .action
= WSM_FILTER_ACTION_FILTER_IN
,
60 .type
= __cpu_to_le16(ETH_P_IP
),
63 .action
= WSM_FILTER_ACTION_FILTER_IN
,
64 .type
= __cpu_to_le16(ETH_P_PAE
),
67 .action
= WSM_FILTER_ACTION_FILTER_IN
,
68 .type
= __cpu_to_le16(ETH_P_WAPI
),
71 .action
= WSM_FILTER_ACTION_FILTER_IN
,
72 .type
= __cpu_to_le16(ETH_P_ARP
),
77 static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off
= {
82 struct cw1200_suspend_state
{
83 unsigned long bss_loss_tmo
;
84 unsigned long join_tmo
;
85 unsigned long direct_probe
;
86 unsigned long link_id_gc
;
91 static void cw1200_pm_stay_awake_tmo(struct timer_list
*unused
)
93 /* XXX what's the point of this ? */
96 int cw1200_pm_init(struct cw1200_pm_state
*pm
,
97 struct cw1200_common
*priv
)
99 spin_lock_init(&pm
->lock
);
101 timer_setup(&pm
->stay_awake
, cw1200_pm_stay_awake_tmo
, 0);
106 void cw1200_pm_deinit(struct cw1200_pm_state
*pm
)
108 del_timer_sync(&pm
->stay_awake
);
111 void cw1200_pm_stay_awake(struct cw1200_pm_state
*pm
,
115 spin_lock_bh(&pm
->lock
);
116 cur_tmo
= pm
->stay_awake
.expires
- jiffies
;
117 if (!timer_pending(&pm
->stay_awake
) || cur_tmo
< (long)tmo
)
118 mod_timer(&pm
->stay_awake
, jiffies
+ tmo
);
119 spin_unlock_bh(&pm
->lock
);
122 static long cw1200_suspend_work(struct delayed_work
*work
)
124 int ret
= cancel_delayed_work(work
);
127 /* Timer is pending */
128 tmo
= work
->timer
.expires
- jiffies
;
137 static int cw1200_resume_work(struct cw1200_common
*priv
,
138 struct delayed_work
*work
,
144 return queue_delayed_work(priv
->workqueue
, work
, tmo
);
147 int cw1200_can_suspend(struct cw1200_common
*priv
)
149 if (atomic_read(&priv
->bh_rx
)) {
150 wiphy_dbg(priv
->hw
->wiphy
, "Suspend interrupted.\n");
155 EXPORT_SYMBOL_GPL(cw1200_can_suspend
);
157 int cw1200_wow_suspend(struct ieee80211_hw
*hw
, struct cfg80211_wowlan
*wowlan
)
159 struct cw1200_common
*priv
= hw
->priv
;
160 struct cw1200_pm_state
*pm_state
= &priv
->pm_state
;
161 struct cw1200_suspend_state
*state
;
164 spin_lock_bh(&pm_state
->lock
);
165 ret
= timer_pending(&pm_state
->stay_awake
);
166 spin_unlock_bh(&pm_state
->lock
);
170 /* Do not suspend when datapath is not idle */
171 if (priv
->tx_queue_stats
.num_queued
)
174 /* Make sure there is no configuration requests in progress. */
175 if (!mutex_trylock(&priv
->conf_mutex
))
178 /* Ensure pending operations are done.
179 * Note also that wow_suspend must return in ~2.5sec, before
180 * watchdog is triggered.
182 if (priv
->channel_switch_in_progress
)
185 /* Do not suspend when join is pending */
186 if (priv
->join_pending
)
189 /* Do not suspend when scanning */
190 if (down_trylock(&priv
->scan
.lock
))
194 wsm_lock_tx_async(priv
);
196 /* Wait to avoid possible race with bh code.
197 * But do not wait too long...
199 if (wait_event_timeout(priv
->bh_evt_wq
,
200 !priv
->hw_bufs_used
, HZ
/ 10) <= 0)
204 wsm_set_udp_port_filter(priv
, &cw1200_udp_port_filter_on
.hdr
);
206 /* Set ethernet frame type filter */
207 wsm_set_ether_type_filter(priv
, &cw1200_ether_type_filter_on
.hdr
);
210 state
= kzalloc(sizeof(struct cw1200_suspend_state
), GFP_KERNEL
);
214 /* Change to legacy PS while going to suspend */
215 if (!priv
->vif
->p2p
&&
216 priv
->join_status
== CW1200_JOIN_STATUS_STA
&&
217 priv
->powersave_mode
.mode
!= WSM_PSM_PS
) {
218 state
->prev_ps_mode
= priv
->powersave_mode
.mode
;
219 priv
->powersave_mode
.mode
= WSM_PSM_PS
;
220 cw1200_set_pm(priv
, &priv
->powersave_mode
);
221 if (wait_event_interruptible_timeout(priv
->ps_mode_switch_done
,
222 !priv
->ps_mode_switch_in_progress
, 1*HZ
) <= 0) {
227 /* Store delayed work states. */
228 state
->bss_loss_tmo
=
229 cw1200_suspend_work(&priv
->bss_loss_work
);
231 cw1200_suspend_work(&priv
->join_timeout
);
232 state
->direct_probe
=
233 cw1200_suspend_work(&priv
->scan
.probe_work
);
235 cw1200_suspend_work(&priv
->link_id_gc_work
);
237 cancel_delayed_work_sync(&priv
->clear_recent_scan_work
);
238 atomic_set(&priv
->recent_scan
, 0);
240 /* Enable beacon skipping */
241 if (priv
->join_status
== CW1200_JOIN_STATUS_STA
&&
242 priv
->join_dtim_period
&&
243 !priv
->has_multicast_subscription
) {
244 state
->beacon_skipping
= true;
245 wsm_set_beacon_wakeup_period(priv
,
246 priv
->join_dtim_period
,
247 CW1200_BEACON_SKIPPING_MULTIPLIER
* priv
->join_dtim_period
);
250 /* Stop serving thread */
251 if (cw1200_bh_suspend(priv
))
254 ret
= timer_pending(&priv
->mcast_timeout
);
258 /* Store suspend state */
259 pm_state
->suspend_state
= state
;
261 /* Enable IRQ wake */
262 ret
= priv
->hwbus_ops
->power_mgmt(priv
->hwbus_priv
, true);
264 wiphy_err(priv
->hw
->wiphy
,
265 "PM request failed: %d. WoW is disabled.\n", ret
);
266 cw1200_wow_resume(hw
);
270 /* Force resume if event is coming from the device. */
271 if (atomic_read(&priv
->bh_rx
)) {
272 cw1200_wow_resume(hw
);
279 WARN_ON(cw1200_bh_resume(priv
));
281 cw1200_resume_work(priv
, &priv
->bss_loss_work
,
282 state
->bss_loss_tmo
);
283 cw1200_resume_work(priv
, &priv
->join_timeout
,
285 cw1200_resume_work(priv
, &priv
->scan
.probe_work
,
286 state
->direct_probe
);
287 cw1200_resume_work(priv
, &priv
->link_id_gc_work
,
292 wsm_set_udp_port_filter(priv
, &cw1200_udp_port_filter_off
);
293 wsm_set_ether_type_filter(priv
, &cw1200_ether_type_filter_off
);
296 up(&priv
->scan
.lock
);
298 mutex_unlock(&priv
->conf_mutex
);
302 int cw1200_wow_resume(struct ieee80211_hw
*hw
)
304 struct cw1200_common
*priv
= hw
->priv
;
305 struct cw1200_pm_state
*pm_state
= &priv
->pm_state
;
306 struct cw1200_suspend_state
*state
;
308 state
= pm_state
->suspend_state
;
309 pm_state
->suspend_state
= NULL
;
311 /* Disable IRQ wake */
312 priv
->hwbus_ops
->power_mgmt(priv
->hwbus_priv
, false);
314 /* Scan.lock must be released before BH is resumed other way
315 * in case when BSS_LOST command arrived the processing of the
316 * command will be delayed.
318 up(&priv
->scan
.lock
);
320 /* Resume BH thread */
321 WARN_ON(cw1200_bh_resume(priv
));
323 /* Restores previous PS mode */
324 if (!priv
->vif
->p2p
&& priv
->join_status
== CW1200_JOIN_STATUS_STA
) {
325 priv
->powersave_mode
.mode
= state
->prev_ps_mode
;
326 cw1200_set_pm(priv
, &priv
->powersave_mode
);
329 if (state
->beacon_skipping
) {
330 wsm_set_beacon_wakeup_period(priv
, priv
->beacon_int
*
331 priv
->join_dtim_period
>
332 MAX_BEACON_SKIP_TIME_MS
? 1 :
333 priv
->join_dtim_period
, 0);
334 state
->beacon_skipping
= false;
337 /* Resume delayed work */
338 cw1200_resume_work(priv
, &priv
->bss_loss_work
,
339 state
->bss_loss_tmo
);
340 cw1200_resume_work(priv
, &priv
->join_timeout
,
342 cw1200_resume_work(priv
, &priv
->scan
.probe_work
,
343 state
->direct_probe
);
344 cw1200_resume_work(priv
, &priv
->link_id_gc_work
,
347 /* Remove UDP port filter */
348 wsm_set_udp_port_filter(priv
, &cw1200_udp_port_filter_off
);
350 /* Remove ethernet frame type filter */
351 wsm_set_ether_type_filter(priv
, &cw1200_ether_type_filter_off
);
353 /* Unlock datapath */
356 /* Unlock configuration mutex */
357 mutex_unlock(&priv
->conf_mutex
);