blob: 65e3a20e81c735377c16c7f798bf9f0db451b597 [file] [log] [blame]
Mukul Sharmad75a6672017-06-22 15:40:53 +05301/*
2* Copyright (c) 2017 The Linux Foundation. All rights reserved.
3*
4* Permission to use, copy, modify, and/or distribute this software for
5* any purpose with or without fee is hereby granted, provided that the
6* above copyright notice and this permission notice appear in all
7* copies.
8*
9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16* PERFORMANCE OF THIS SOFTWARE.
17*/
18/**
19 * DOC: Define API's for suspend / resume handling
20 */
21
22#include "wlan_pmo_wow.h"
23#include "wlan_pmo_tgt_api.h"
24#include "wlan_pmo_main.h"
25#include "wlan_pmo_obj_mgmt_public_struct.h"
26#include "wlan_pmo_lphb.h"
27#include "wlan_pmo_suspend_resume.h"
28#include "cdp_txrx_ops.h"
29#include "cdp_txrx_misc.h"
30#include "cdp_txrx_flow_ctrl_legacy.h"
31#include "hif.h"
32#include "htc_api.h"
33#include "wlan_pmo_obj_mgmt_api.h"
34#include <wlan_scan_ucfg_api.h>
35#include "cds_api.h"
36#include "wlan_pmo_static_config.h"
37
38/**
39 * pmo_core_calculate_listen_interval() - Calculate vdev listen interval
40 * @vdev: objmgr vdev handle
41 * @vdev_ctx: pmo vdev priv ctx
42 * @listen_interval: listen interval which is computed for vdev
43 *
44 * Return: QDF_STATUS
45 */
46static QDF_STATUS pmo_core_calculate_listen_interval(
47 struct wlan_objmgr_vdev *vdev,
48 struct pmo_vdev_priv_obj *vdev_ctx,
49 uint32_t *listen_interval)
50{
51 uint32_t max_mod_dtim;
52 uint32_t beacon_interval_mod;
53 struct pmo_psoc_cfg *psoc_cfg = &vdev_ctx->pmo_psoc_ctx->psoc_cfg;
54
55 if (psoc_cfg->sta_dynamic_dtim) {
56 *listen_interval = psoc_cfg->sta_dynamic_dtim;
57 } else if ((psoc_cfg->sta_mod_dtim) &&
58 (psoc_cfg->sta_max_li_mod_dtim)) {
59 /*
60 * When the system is in suspend
61 * (maximum beacon will be at 1s == 10)
62 * If maxModulatedDTIM ((MAX_LI_VAL = 10) / AP_DTIM)
63 * equal or larger than MDTIM
64 * (configured in WCNSS_qcom_cfg.ini)
65 * Set LI to MDTIM * AP_DTIM
66 * If Dtim = 2 and Mdtim = 2 then LI is 4
67 * Else
68 * Set LI to maxModulatedDTIM * AP_DTIM
69 */
70 beacon_interval_mod =
71 pmo_core_get_vdev_beacon_interval(vdev) / 100;
72 if (beacon_interval_mod == 0)
73 beacon_interval_mod = 1;
74
75 max_mod_dtim = psoc_cfg->sta_max_li_mod_dtim /
76 (pmo_core_get_vdev_dtim_period(vdev)
77 * beacon_interval_mod);
78
79 if (max_mod_dtim <= 0)
80 max_mod_dtim = 1;
81
82 if (max_mod_dtim >= psoc_cfg->sta_mod_dtim) {
83 *listen_interval =
84 (psoc_cfg->sta_mod_dtim *
85 pmo_core_get_vdev_dtim_period(vdev));
86 } else {
87 *listen_interval =
88 (max_mod_dtim *
89 pmo_core_get_vdev_dtim_period(vdev));
90 }
91 } else {
92 return QDF_STATUS_E_FAULT;
93 }
94 return QDF_STATUS_SUCCESS;
95}
96
97/**
98 * pmo_core_set_vdev_suspend_dtim() - set suspend dtim parameters in fw
99 * @psoc: objmgr psoc handle
100 * @vdev: objmgr vdev handle
101 * @vdev_ctx: pmo vdev priv ctx
102 *
103 * Return: none
104 */
105static void pmo_core_set_vdev_suspend_dtim(struct wlan_objmgr_psoc *psoc,
106 struct wlan_objmgr_vdev *vdev,
107 struct pmo_vdev_priv_obj *vdev_ctx)
108{
109 uint32_t listen_interval;
110 QDF_STATUS ret;
111 uint8_t vdev_id;
112 enum pmo_power_save_qpower_mode qpower_config;
113 enum tQDF_ADAPTER_MODE opmode = pmo_core_get_vdev_op_mode(vdev);
114
115 qpower_config = pmo_core_psoc_get_qpower_config(psoc);
116 vdev_id = pmo_vdev_get_id(vdev);
117 if (PMO_VDEV_IN_STA_MODE(opmode) &&
118 pmo_core_get_vdev_dtim_period(vdev) != 0) {
119 /* calculate listen interval */
120 ret = pmo_core_calculate_listen_interval(vdev, vdev_ctx,
121 &listen_interval);
122 if (ret != QDF_STATUS_SUCCESS) {
123 /* even it fails continue fwr will take default LI */
124 pmo_info("Fail to calculate listen interval");
125 }
126
127 ret = pmo_tgt_vdev_update_param_req(vdev,
128 pmo_vdev_param_listen_interval,
129 listen_interval);
130 if (QDF_IS_STATUS_ERROR(ret)) {
131 /* even it fails continue fwr will take default LI */
132 pmo_info("Failed to Set Listen Interval vdevId %d",
133 vdev_id);
134 }
135 pmo_debug("Set Listen Interval vdevId %d Listen Intv %d",
136 vdev_id, listen_interval);
137
138 if (qpower_config) {
139 pmo_debug("disable Qpower in suspend mode!");
140 ret = pmo_tgt_send_vdev_sta_ps_param(vdev,
141 pmo_sta_ps_enable_qpower, 0);
142 if (QDF_IS_STATUS_ERROR(ret))
143 pmo_info("Failed to disable Qpower in suspend mode!");
144 }
145
146 ret = pmo_tgt_vdev_update_param_req(vdev,
147 pmo_vdev_param_dtim_policy,
148 pmo_normal_dtim);
149 if (QDF_IS_STATUS_ERROR(ret))
150 pmo_info("Failed to Set to Normal DTIM vdevId %d",
151 vdev_id);
152
153 /* Set it to Normal DTIM */
154 pmo_core_vdev_set_dtim_policy(vdev, pmo_normal_dtim);
155 pmo_debug("Set DTIM Policy to Normal Dtim vdevId %d", vdev_id);
156 }
157}
158
159/**
160 * pmo_core_set_suspend_dtim() - set suspend dtim
161 * @psoc: objmgr psoc handle
162 *
163 * Return: none
164 */
165static void pmo_core_set_suspend_dtim(struct wlan_objmgr_psoc *psoc)
166{
167 uint8_t vdev_id;
168 struct wlan_objmgr_vdev *vdev;
169 struct pmo_vdev_priv_obj *vdev_ctx;
170 bool alt_mdtim_enabled;
171 QDF_STATUS status;
172
173 /* Iterate through VDEV list */
174 for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
175 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
176 if (!vdev)
177 continue;
178
179 status = pmo_vdev_get_ref(vdev);
180 if (QDF_IS_STATUS_ERROR(status))
181 continue;
182
183 vdev_ctx = pmo_vdev_get_priv(vdev);
184 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
185 alt_mdtim_enabled = vdev_ctx->alt_modulated_dtim_enable;
186 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
187
188 if (!alt_mdtim_enabled)
189 pmo_core_set_vdev_suspend_dtim(psoc, vdev, vdev_ctx);
190
191 pmo_vdev_put_ref(vdev);
192 }
193}
194
195/**
196 * pmo_core_update_wow_bus_suspend() - set wow bus suspend flag
197 * @psoc: objmgr psoc handle
198 * @psoc_ctx: pmo psoc priv ctx
199 * @val: true for enable else false
200 * Return: none
201 */
202static inline
203void pmo_core_update_wow_bus_suspend(struct wlan_objmgr_psoc *psoc,
204 struct pmo_psoc_priv_obj *psoc_ctx, int val)
205{
206 qdf_spin_lock_bh(&psoc_ctx->lock);
207 psoc_ctx->wow.is_wow_bus_suspended = val;
208 qdf_spin_unlock_bh(&psoc_ctx->lock);
209 pmo_tgt_psoc_update_wow_bus_suspend_state(psoc, val);
210}
211
212/* Define for conciseness */
213#define BM_LEN PMO_WOW_MAX_EVENT_BM_LEN
214#define EV_NLO WOW_NLO_SCAN_COMPLETE_EVENT
215#define EV_PWR WOW_CHIP_POWER_FAILURE_DETECT_EVENT
216
217void pmo_core_configure_dynamic_wake_events(struct wlan_objmgr_psoc *psoc)
218{
219 int vdev_id;
220 uint32_t adapter_type;
221 uint32_t enable_mask[BM_LEN];
222 uint32_t disable_mask[BM_LEN];
223 struct wlan_objmgr_vdev *vdev;
224 struct pmo_psoc_priv_obj *psoc_ctx;
225 bool enable_configured;
226 bool disable_configured;
227
228 /* Iterate through VDEV list */
229 for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
230
231 enable_configured = false;
232 disable_configured = false;
233
234 qdf_mem_set(enable_mask, sizeof(uint32_t) * BM_LEN, 0);
235 qdf_mem_set(disable_mask, sizeof(uint32_t) * BM_LEN, 0);
236
237 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
238 if (!vdev)
239 continue;
240
241 if (ucfg_scan_get_pno_in_progress(vdev)) {
242 if (ucfg_scan_get_pno_match(vdev)) {
243 pmo_set_wow_event_bitmap(EV_NLO,
244 BM_LEN,
245 enable_mask);
246 enable_configured = true;
247 } else {
248 pmo_set_wow_event_bitmap(EV_NLO,
249 BM_LEN,
250 disable_mask);
251 disable_configured = true;
252 }
253 }
254
255 adapter_type = pmo_get_vdev_opmode(vdev);
256
257 psoc_ctx = pmo_psoc_get_priv(psoc);
258
259 if (psoc_ctx->psoc_cfg.auto_power_save_fail_mode &&
260 (adapter_type == QDF_STA_MODE ||
261 adapter_type == QDF_P2P_CLIENT_MODE)
262 ) {
263 if (psoc_ctx->is_device_in_low_pwr_mode &&
264 psoc_ctx->is_device_in_low_pwr_mode(vdev_id))
265 pmo_set_wow_event_bitmap(EV_PWR,
266 BM_LEN,
267 enable_mask);
268 pmo_core_enable_wakeup_event(psoc, vdev_id,
269 enable_mask);
270 enable_configured = true;
271 }
272 if (enable_configured)
273 pmo_core_enable_wakeup_event(psoc, vdev_id,
274 enable_mask);
275
276 if (disable_configured)
277 pmo_core_disable_wakeup_event(psoc, vdev_id,
278 disable_mask);
279 }
280
281}
282
283/**
284 * pmo_core_psoc_configure_suspend(): configure suspend req events
285 * @psoc: objmgr psoc
286 *
287 * Responsibility of the caller to take the psoc reference.
288 *
289 * Return: QDF_STATUS_SUCCESS for success or error code
290 */
291static QDF_STATUS pmo_core_psoc_configure_suspend(struct wlan_objmgr_psoc *psoc)
292{
293 struct pmo_psoc_priv_obj *psoc_ctx;
294
295 PMO_ENTER();
296
297 psoc_ctx = pmo_psoc_get_priv(psoc);
298
299 if (pmo_core_is_wow_applicable(psoc)) {
300 pmo_info("WOW Suspend");
301 pmo_core_apply_lphb(psoc);
302
303 pmo_core_configure_dynamic_wake_events(psoc);
304 pmo_core_update_wow_enable(psoc_ctx, true);
305 pmo_core_update_wow_enable_cmd_sent(psoc_ctx, false);
306 }
307
308 pmo_core_set_suspend_dtim(psoc);
309
310 /*
311 * To handle race between hif_pci_suspend and unpause/pause tx handler.
312 * This happens when host sending WMI_WOW_ENABLE_CMDID to FW and receive
313 * WMI_TX_PAUSE_EVENT with ACTON_UNPAUSE almost at same time.
314 */
315 pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, true);
316
317 PMO_EXIT();
318
319 return QDF_STATUS_SUCCESS;
320}
321
322QDF_STATUS pmo_core_psoc_user_space_suspend_req(struct wlan_objmgr_psoc *psoc,
323 enum qdf_suspend_type type)
324{
325 QDF_STATUS status;
326
327 PMO_ENTER();
328
329 status = pmo_psoc_get_ref(psoc);
330 if (status != QDF_STATUS_SUCCESS) {
331 pmo_err("pmo cannot get the reference out of psoc");
332 goto out;
333 }
334
335 /* Suspend all components before sending target suspend command */
336 status = pmo_suspend_all_components(psoc, type);
337 if (status != QDF_STATUS_SUCCESS) {
338 pmo_err("Failed to suspend all component");
339 goto dec_psoc_ref;
340 }
341
342 status = pmo_core_psoc_configure_suspend(psoc);
343 if (status != QDF_STATUS_SUCCESS)
344 pmo_err("Failed to configure suspend");
345
346dec_psoc_ref:
347 pmo_psoc_put_ref(psoc);
348out:
349 PMO_EXIT();
350
351 return status;
352}
353
354/**
355 * pmo_core_set_vdev_resume_dtim() - set resume dtim parameters in fw
356 * @psoc: objmgr psoc handle
357 * @vdev: objmgr vdev handle
358 * @vdev_ctx: pmo vdev priv ctx
359 *
360 * Return: none
361 */
362static void pmo_core_set_vdev_resume_dtim(struct wlan_objmgr_psoc *psoc,
363 struct wlan_objmgr_vdev *vdev,
364 struct pmo_vdev_priv_obj *vdev_ctx)
365{
366 enum pmo_power_save_qpower_mode qpower_config;
367 QDF_STATUS ret;
368 uint8_t vdev_id;
369 enum tQDF_ADAPTER_MODE opmode = pmo_core_get_vdev_op_mode(vdev);
370 uint32_t cfg_data_val = 0;
371
372 qpower_config = pmo_core_psoc_get_qpower_config(psoc);
373 vdev_id = pmo_vdev_get_id(vdev);
374 if ((PMO_VDEV_IN_STA_MODE(opmode)) &&
375 (pmo_core_vdev_get_dtim_policy(vdev) == pmo_normal_dtim)) {
376/*
377 if (!mac) {
378 WMA_LOGE(FL("Failed to get mac context"));
379 return;
380 }
381 if ((wlan_cfg_get_int(mac, WNI_CFG_LISTEN_INTERVAL,
382 &cfg_data_val) != eSIR_SUCCESS)) {
383 pmo_err("Failed to get value for listen interval");
384 cfg_data_val = POWERSAVE_DEFAULT_LISTEN_INTERVAL;
385 }
386*/
387 cfg_data_val = 1;
388 ret = pmo_tgt_vdev_update_param_req(vdev,
389 pmo_vdev_param_listen_interval, cfg_data_val);
390 if (QDF_IS_STATUS_ERROR(ret)) {
391 /* Even it fails continue Fw will take default LI */
392 pmo_err("Failed to Set Listen Interval vdevId %d",
393 vdev_id);
394 }
395 pmo_debug("Set Listen Interval vdevId %d Listen Intv %d",
396 vdev_id, cfg_data_val);
397
398 ret = pmo_tgt_vdev_update_param_req(vdev,
399 pmo_vdev_param_dtim_policy,
400 pmo_stick_dtim);
401 if (QDF_IS_STATUS_ERROR(ret)) {
402 /* Set it back to Stick DTIM */
403 pmo_err("Failed to Set to Stick DTIM vdevId %d",
404 vdev_id);
405 }
406 pmo_core_vdev_set_dtim_policy(vdev, pmo_stick_dtim);
407 pmo_debug("Set DTIM Policy to Stick Dtim vdevId %d", vdev_id);
408
409 if (qpower_config) {
410 pmo_debug("enable Qpower in resume mode!");
411 ret = pmo_tgt_send_vdev_sta_ps_param(vdev,
412 pmo_sta_ps_enable_qpower, qpower_config);
413 if (QDF_IS_STATUS_ERROR(ret))
414 pmo_err("Failed to enable Qpower in resume");
415 }
416 }
417}
418
419/**
420 * pmo_core_set_resume_dtim() - set resume time dtim
421 * @psoc: objmgr psoc handle
422 *
423 * Return: none
424 */
425static void pmo_core_set_resume_dtim(struct wlan_objmgr_psoc *psoc)
426{
427 uint8_t vdev_id;
428 struct wlan_objmgr_vdev *vdev;
429 struct pmo_vdev_priv_obj *vdev_ctx;
430 bool alt_mdtim_enabled;
431 QDF_STATUS status;
432
433 /* Iterate through VDEV list */
434 for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
435 vdev = pmo_psoc_get_vdev(psoc, vdev_id);
436 if (!vdev)
437 continue;
438
439 status = pmo_vdev_get_ref(vdev);
440 if (QDF_IS_STATUS_ERROR(status))
441 continue;
442
443 vdev_ctx = pmo_vdev_get_priv(vdev);
444 qdf_spin_lock_bh(&vdev_ctx->pmo_vdev_lock);
445 alt_mdtim_enabled = vdev_ctx->alt_modulated_dtim_enable;
446 qdf_spin_unlock_bh(&vdev_ctx->pmo_vdev_lock);
447
448 if (!alt_mdtim_enabled)
449 pmo_core_set_vdev_resume_dtim(psoc, vdev, vdev_ctx);
450
451 pmo_vdev_put_ref(vdev);
452 }
453}
454
455/**
456 * pmo_unpause_vdev - unpause all vdev
457 * @psoc: objmgr psoc handle
458 *
459 * unpause all vdev aftter resume/coming out of wow mode
460 *
461 * Return: none
462 */
463static void pmo_unpause_all_vdev(struct wlan_objmgr_psoc *psoc,
464 struct pmo_psoc_priv_obj *psoc_ctx)
465{
466 uint8_t vdev_id;
467 struct wlan_objmgr_psoc_objmgr *objmgr;
468 struct wlan_objmgr_vdev *vdev;
469
470 /* Iterate through VDEV list */
471 for (vdev_id = 0; vdev_id < WLAN_UMAC_PSOC_MAX_VDEVS; vdev_id++) {
472 wlan_psoc_obj_lock(psoc);
473 objmgr = &psoc->soc_objmgr;
474 if (!objmgr->wlan_vdev_list[vdev_id]) {
475 wlan_psoc_obj_unlock(psoc);
476 continue;
477 }
478 vdev = objmgr->wlan_vdev_list[vdev_id];
479 wlan_psoc_obj_unlock(psoc);
480 if (vdev) {
481#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2)
482 /*
483 * When host resume, by default,
484 * unpause all active vdev
485 */
486 if (pmo_core_vdev_get_pause_bitmap(psoc_ctx, vdev_id)) {
487 cdp_fc_vdev_unpause(
488 pmo_core_psoc_get_dp_handle(psoc),
489 pmo_core_vdev_get_dp_handle(vdev),
490 0xffffffff);
491 if (psoc_ctx->pause_bitmap_notifier)
492 psoc_ctx->pause_bitmap_notifier(vdev_id,
493 0);
494 }
495#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */
496 }
497 }
498}
499
500/**
501 * pmo_core_psoc_configure_resume(): configure events after bus resume
502 * @psoc: objmgr psoc
503 *
504 * Responsibility of the caller to take the psoc reference.
505 *
506 * Return: QDF_STATUS_SUCCESS for success or error code
507 */
508static QDF_STATUS pmo_core_psoc_configure_resume(struct wlan_objmgr_psoc *psoc)
509{
510 struct pmo_psoc_priv_obj *psoc_ctx;
511
512 PMO_ENTER();
513
514 psoc_ctx = pmo_psoc_get_priv(psoc);
515
516 pmo_core_set_resume_dtim(psoc);
517 pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, false);
518 pmo_unpause_all_vdev(psoc, psoc_ctx);
519
520 PMO_EXIT();
521
522 return QDF_STATUS_SUCCESS;
523}
524
525QDF_STATUS pmo_core_psoc_user_space_resume_req(struct wlan_objmgr_psoc *psoc,
526 enum qdf_suspend_type type)
527{
528 QDF_STATUS status = QDF_STATUS_SUCCESS;
529
530 PMO_ENTER();
531
532 status = pmo_psoc_get_ref(psoc);
533 if (status != QDF_STATUS_SUCCESS) {
534 pmo_err("pmo cannot get the reference out of psoc");
535 goto out;
536 }
537
538 /* Resume all components */
539 status = pmo_resume_all_components(psoc, type);
540 if (status != QDF_STATUS_SUCCESS) {
541 pmo_err("Failed to resume all the components");
542 goto dec_psoc_ref;
543 }
544
545 status = pmo_core_psoc_configure_resume(psoc);
546 if (status != QDF_STATUS_SUCCESS)
547 pmo_err("Failed to configure resume");
548
549dec_psoc_ref:
550 pmo_psoc_put_ref(psoc);
551out:
552 PMO_EXIT();
553
554 return status;
555}
556
557/**
558 * pmo_core_enable_wow_in_fw() - enable wow in fw
559 * @psoc: objmgr psoc handle
560 * @psoc_ctx: pmo psoc private ctx
561 * @wow_params: collection of wow enable override parameters
562 *
563 * Return: QDF status
564 */
565static
566QDF_STATUS pmo_core_enable_wow_in_fw(struct wlan_objmgr_psoc *psoc,
567 struct pmo_psoc_priv_obj *psoc_ctx,
568 struct pmo_wow_enable_params *wow_params)
569{
570 int host_credits, wmi_pending_cmds;
571 struct pmo_wow_cmd_params param = {0};
572 QDF_STATUS status;
573
574 PMO_ENTER();
575 qdf_event_reset(&psoc_ctx->wow.target_suspend);
576 pmo_core_set_wow_nack(psoc_ctx, false);
577 host_credits = pmo_tgt_psoc_get_host_credits(psoc);
578 wmi_pending_cmds = pmo_tgt_psoc_get_pending_cmnds(psoc);
579 pmo_debug("Credits:%d; Pending_Cmds: %d",
580 host_credits, wmi_pending_cmds);
581
582 param.enable = true;
583 if (wow_params->is_unit_test)
584 param.flags = WMI_WOW_FLAG_UNIT_TEST_ENABLE;
585
586 switch (wow_params->interface_pause) {
587 default:
588 pmo_err("Invalid interface pause setting: %d",
589 wow_params->interface_pause);
590 /* intentional fall-through to default */
591 case PMO_WOW_INTERFACE_PAUSE_DEFAULT:
592 param.can_suspend_link =
593 htc_can_suspend_link(
594 pmo_core_psoc_get_htc_handle(psoc));
595 break;
596 case PMO_WOW_INTERFACE_PAUSE_ENABLE:
597 param.can_suspend_link = true;
598 break;
599 case PMO_WOW_INTERFACE_PAUSE_DISABLE:
600 param.can_suspend_link = false;
601 break;
602 }
603
604 switch (wow_params->resume_trigger) {
605 default:
606 pmo_err("Invalid resume trigger setting: %d",
607 wow_params->resume_trigger);
608 /* intentional fall-through to default */
609 case PMO_WOW_RESUME_TRIGGER_DEFAULT:
610 case PMO_WOW_RESUME_TRIGGER_GPIO:
611 /*
612 * GPIO is currently implicit. This means you can't actually
613 * force GPIO if a platform's default wake trigger is HTC wakeup
614 */
615 break;
616 case PMO_WOW_RESUME_TRIGGER_HTC_WAKEUP:
617 param.flags |= WMI_WOW_FLAG_DO_HTC_WAKEUP;
618 break;
619 }
620
621 status = pmo_tgt_psoc_send_wow_enable_req(psoc, &param);
622 if (status != QDF_STATUS_SUCCESS) {
623 pmo_err("Failed to enable wow in fw");
624 goto out;
625 }
626
627 pmo_tgt_update_target_suspend_flag(psoc, true);
628
629 if (qdf_wait_single_event(&psoc_ctx->wow.target_suspend,
630 PMO_TGT_SUSPEND_COMPLETE_TIMEOUT)
631 != QDF_STATUS_SUCCESS) {
632 pmo_err("Failed to receive WoW Enable Ack from FW");
633 pmo_err("Credits:%d; Pending_Cmds: %d",
634 pmo_tgt_psoc_get_host_credits(psoc),
635 pmo_tgt_psoc_get_pending_cmnds(psoc));
636 pmo_tgt_update_target_suspend_flag(psoc, false);
637 status = QDF_STATUS_E_FAILURE;
638 QDF_BUG(0);
639 goto out;
640 }
641
642 if (pmo_core_get_wow_nack(psoc_ctx)) {
643 pmo_err("FW not ready to WOW");
644 pmo_tgt_update_target_suspend_flag(psoc, false);
645 status = QDF_STATUS_E_AGAIN;
646 goto out;
647 }
648
649 host_credits = pmo_tgt_psoc_get_host_credits(psoc);
650 wmi_pending_cmds = pmo_tgt_psoc_get_pending_cmnds(psoc);
651
652 if (host_credits < PMO_WOW_REQUIRED_CREDITS) {
653 pmo_err("No Credits after HTC ACK:%d, pending_cmds:%d,"
654 "cannot resume back", host_credits, wmi_pending_cmds);
655 htc_dump_counter_info(pmo_core_psoc_get_htc_handle(psoc));
656/*
657 if (!cds_is_driver_recovering())
658 QDF_BUG(0);
659 else
660 pmo_err("SSR in progress, ignore no credit issue");
661*/
662 }
663 pmo_debug("WOW enabled successfully in fw: credits:%d pending_cmds: %d",
664 host_credits, wmi_pending_cmds);
665
666 pmo_core_update_wow_enable_cmd_sent(psoc_ctx, true);
667out:
668 PMO_EXIT();
669
670 return status;
671}
672
673QDF_STATUS pmo_core_psoc_suspend_target(struct wlan_objmgr_psoc *psoc,
674 int disable_target_intr)
675{
676 QDF_STATUS status;
677 struct pmo_suspend_params param;
678 struct pmo_psoc_priv_obj *psoc_ctx;
679
680 PMO_ENTER();
681
682 psoc_ctx = pmo_psoc_get_priv(psoc);
683
684 qdf_event_reset(&psoc_ctx->wow.target_suspend);
685 param.disable_target_intr = disable_target_intr;
686 status = pmo_tgt_psoc_send_supend_req(psoc, &param);
687 if (status != QDF_STATUS_SUCCESS)
688 goto out;
689
690 pmo_tgt_update_target_suspend_flag(psoc, true);
691
692 if (qdf_wait_single_event(&psoc_ctx->wow.target_suspend,
693 PMO_TGT_SUSPEND_COMPLETE_TIMEOUT)
694 != QDF_STATUS_SUCCESS) {
695 status = QDF_STATUS_E_TIMEOUT;
696 pmo_err("Failed to get ACK from firmware for pdev suspend");
697 pmo_tgt_update_target_suspend_flag(psoc, false);
698 /* wma_suspend_target_timeout(pmac->sme.enableSelfRecovery); */
699 }
700out:
701 PMO_EXIT();
702
703 return status;
704}
705
706QDF_STATUS pmo_core_psoc_bus_suspend_req(struct wlan_objmgr_psoc *psoc,
707 enum qdf_suspend_type type,
708 struct pmo_wow_enable_params *wow_params)
709{
710 struct pmo_psoc_priv_obj *psoc_ctx;
711 QDF_STATUS status;
712 bool wow_mode_selected = false;
713
714 PMO_ENTER();
715 if (!psoc) {
716 pmo_err("psoc is NULL");
717 status = QDF_STATUS_E_NULL_VALUE;
718 goto out;
719 }
720
721 if (!wow_params) {
722 pmo_err("wow_params is NULL");
723 status = QDF_STATUS_E_NULL_VALUE;
724 goto out;
725 }
726
727 status = pmo_psoc_get_ref(psoc);
728 if (status != QDF_STATUS_SUCCESS) {
729 pmo_err("pmo cannot get the reference out of psoc");
730 goto out;
731 }
732
733 psoc_ctx = pmo_psoc_get_priv(psoc);
734
735/* TODO - scan manager need to provide the below public api
736 if (wma_check_scan_in_progress(handle)) {
737 pmo_err("Scan in progress. Aborting suspend");
738 status = QDF_STATUS_E_NULL_VALUE;
739 goto out;
740 }
741*/
742
743 wow_mode_selected = pmo_core_is_wow_enabled(psoc_ctx);
744 pmo_info("wow mode selected %d", wow_mode_selected);
745
746 if (wow_mode_selected)
747 status = pmo_core_enable_wow_in_fw(psoc, psoc_ctx, wow_params);
748 else
749 status = pmo_core_psoc_suspend_target(psoc, 0);
750
751 pmo_psoc_put_ref(psoc);
752out:
753 PMO_EXIT();
754
755 return status;
756}
757
758#ifdef FEATURE_RUNTIME_PM
759QDF_STATUS pmo_core_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc,
760 pmo_pld_auto_suspend_cb pld_cb)
761{
762 void *hif_ctx;
763 void *dp_soc;
764 void *txrx_pdev;
765 void *htc_ctx;
766 QDF_STATUS status;
767 struct pmo_wow_enable_params wow_params = {0};
768
769 PMO_ENTER();
770
771 if (!psoc) {
772 pmo_err("psoc is NULL");
773 status = QDF_STATUS_E_INVAL;
774 goto out;
775 }
776
777 status = pmo_psoc_get_ref(psoc);
778 if (status != QDF_STATUS_SUCCESS) {
779 pmo_err("pmo cannot get the reference out of psoc");
780 goto out;
781 }
782
783 hif_ctx = pmo_core_psoc_get_hif_handle(psoc);
784 dp_soc = pmo_core_psoc_get_dp_handle(psoc);
785 txrx_pdev = pmo_core_psoc_get_txrx_handle(psoc);
786 htc_ctx = pmo_core_psoc_get_htc_handle(psoc);
787 if (!hif_ctx || !dp_soc || !txrx_pdev || !htc_ctx) {
788 pmo_err("Invalid hif: %p, dp: %p, txrx: %p, htc: %p",
789 hif_ctx, dp_soc, txrx_pdev, htc_ctx);
790 status = QDF_STATUS_E_INVAL;
791 goto dec_psoc_ref;
792 }
793
794 wow_params.interface_pause = PMO_WOW_INTERFACE_PAUSE_ENABLE;
795 wow_params.resume_trigger = PMO_WOW_RESUME_TRIGGER_GPIO;
796
797 if (hif_pre_runtime_suspend(hif_ctx))
798 goto runtime_failure;
799
800 status = cdp_runtime_suspend(dp_soc, txrx_pdev);
801 if (status != QDF_STATUS_SUCCESS)
802 goto runtime_failure;
803
804 if (htc_runtime_suspend(htc_ctx))
805 goto cdp_runtime_resume;
806
807 status = pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, true);
808 if (status != QDF_STATUS_SUCCESS)
809 goto resume_htc;
810
811 status = pmo_core_psoc_configure_suspend(psoc);
812 if (status != QDF_STATUS_SUCCESS)
813 goto resume_htc;
814
815 status = pmo_core_psoc_bus_suspend_req(psoc, QDF_RUNTIME_SUSPEND,
816 &wow_params);
817 if (status != QDF_STATUS_SUCCESS)
818 goto pmo_resume_configure;
819
820 if (hif_runtime_suspend(hif_ctx))
821 goto pmo_bus_resume;
822
823 if (pld_cb && pld_cb())
824 goto resume_hif;
825
826 hif_process_runtime_suspend_success(hif_ctx);
827
828 goto dec_psoc_ref;
829
830resume_hif:
831 QDF_BUG(!hif_runtime_resume(hif_ctx));
832
833pmo_bus_resume:
834 QDF_BUG(QDF_STATUS_SUCCESS ==
835 pmo_core_psoc_bus_resume_req(psoc, QDF_RUNTIME_SUSPEND));
836
837pmo_resume_configure:
838 QDF_BUG(QDF_STATUS_SUCCESS ==
839 pmo_core_psoc_configure_resume(psoc));
840
841resume_htc:
842 QDF_BUG(QDF_STATUS_SUCCESS ==
843 pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, false));
844
845 QDF_BUG(!htc_runtime_resume(htc_ctx));
846
847cdp_runtime_resume:
848 QDF_BUG(QDF_STATUS_SUCCESS ==
849 cdp_runtime_resume(dp_soc, txrx_pdev));
850
851runtime_failure:
852 hif_process_runtime_suspend_failure(hif_ctx);
853
854dec_psoc_ref:
855 pmo_psoc_put_ref(psoc);
856
857out:
858 PMO_EXIT();
859
860 return status;
861}
862
863QDF_STATUS pmo_core_psoc_bus_runtime_resume(struct wlan_objmgr_psoc *psoc,
864 pmo_pld_auto_resume_cb pld_cb)
865{
866 void *hif_ctx;
867 void *dp_soc;
868 void *txrx_pdev;
869 void *htc_ctx;
870 QDF_STATUS status;
871
872 PMO_ENTER();
873
874 if (!psoc) {
875 pmo_err("psoc is NULL");
876 status = QDF_STATUS_E_INVAL;
877 goto out;
878 }
879
880 status = pmo_psoc_get_ref(psoc);
881 if (status != QDF_STATUS_SUCCESS) {
882 pmo_err("pmo cannot get the reference out of psoc");
883 goto out;
884 }
885
886 hif_ctx = pmo_core_psoc_get_hif_handle(psoc);
887 dp_soc = pmo_core_psoc_get_dp_handle(psoc);
888 txrx_pdev = pmo_core_psoc_get_txrx_handle(psoc);
889 htc_ctx = pmo_core_psoc_get_htc_handle(psoc);
890 if (!hif_ctx || !dp_soc || !txrx_pdev || !htc_ctx) {
891 pmo_err("Invalid hif: %p, dp: %p, txrx: %p, htc: %p",
892 hif_ctx, dp_soc, txrx_pdev, htc_ctx);
893 status = QDF_STATUS_E_INVAL;
894 goto dec_psoc_ref;
895 }
896
897 hif_pre_runtime_resume(hif_ctx);
898
899 if (pld_cb)
900 QDF_BUG(!pld_cb());
901
902 QDF_BUG(!hif_runtime_resume(hif_ctx));
903
904 status = pmo_core_psoc_bus_resume_req(psoc, QDF_RUNTIME_SUSPEND);
905 QDF_BUG(status == QDF_STATUS_SUCCESS);
906
907 status = pmo_core_psoc_configure_resume(psoc);
908 QDF_BUG(status == QDF_STATUS_SUCCESS);
909
910 status = pmo_tgt_psoc_set_runtime_pm_inprogress(psoc, false);
911 QDF_BUG(status == QDF_STATUS_SUCCESS);
912
913 QDF_BUG(!htc_runtime_resume(htc_ctx));
914
915 status = cdp_runtime_resume(dp_soc, txrx_pdev);
916 QDF_BUG(status == QDF_STATUS_SUCCESS);
917
918 hif_process_runtime_resume_success(hif_ctx);
919
920dec_psoc_ref:
921 pmo_psoc_put_ref(psoc);
922
923out:
924 PMO_EXIT();
925
926 return status;
927}
928#endif
929
930/**
931 * pmo_core_psoc_send_host_wakeup_ind_to_fw() - send wakeup ind to fw
932 * @psoc: objmgr psoc handle
933 * @psoc_ctx: pmo psoc private context
934 *
935 * Sends host wakeup indication to FW. On receiving this indication,
936 * FW will come out of WOW.
937 *
938 * Return: QDF status
939 */
940static
941QDF_STATUS pmo_core_psoc_send_host_wakeup_ind_to_fw(
942 struct wlan_objmgr_psoc *psoc,
943 struct pmo_psoc_priv_obj *psoc_ctx)
944{
945 QDF_STATUS status = QDF_STATUS_SUCCESS;
946
947 PMO_ENTER();
948 qdf_event_reset(&psoc_ctx->wow.target_resume);
949
950 status = pmo_tgt_psoc_send_host_wakeup_ind(psoc);
951 if (status) {
952 status = QDF_STATUS_E_FAILURE;
953 goto out;
954 }
955 pmo_debug("Host wakeup indication sent to fw");
956
957 status = qdf_wait_single_event(&psoc_ctx->wow.target_resume,
958 PMO_RESUME_TIMEOUT);
959 if (status != QDF_STATUS_SUCCESS) {
960 pmo_err("Timeout waiting for resume event from FW");
961 pmo_err("Pending commands %d credits %d",
962 pmo_tgt_psoc_get_pending_cmnds(psoc),
963 pmo_tgt_psoc_get_host_credits(psoc));
964 QDF_BUG(0);
965 } else {
966 pmo_debug("Host wakeup received");
967 }
968
969 if (status == QDF_STATUS_SUCCESS)
970 pmo_tgt_update_target_suspend_flag(psoc, false);
971out:
972 PMO_EXIT();
973
974 return status;
975}
976
977/**
978 * pmo_core_psoc_disable_wow_in_fw() - Disable wow in bus resume context.
979 * @psoc: objmgr psoc handle
980 * @psoc_ctx: pmo psoc private context
981 *
982 * Return: QDF_STATUS_SUCCESS for success or error code
983 */
984static
985QDF_STATUS pmo_core_psoc_disable_wow_in_fw(struct wlan_objmgr_psoc *psoc,
986 struct pmo_psoc_priv_obj *psoc_ctx)
987{
988 QDF_STATUS ret;
989
990 PMO_ENTER();
991 ret = pmo_core_psoc_send_host_wakeup_ind_to_fw(psoc, psoc_ctx);
992 if (ret != QDF_STATUS_SUCCESS)
993 goto out;
994
995 pmo_core_update_wow_enable(psoc_ctx, false);
996 pmo_core_update_wow_enable_cmd_sent(psoc_ctx, false);
997
998 /* To allow the tx pause/unpause events */
999 pmo_core_update_wow_bus_suspend(psoc, psoc_ctx, false);
1000 /* Unpause the vdev as we are resuming */
1001 pmo_unpause_all_vdev(psoc, psoc_ctx);
1002out:
1003 PMO_EXIT();
1004
1005 return ret;
1006}
1007
1008/**
1009 * pmo_core_psoc_resume_target() - resume target
1010 * @psoc: objmgr psoc handle
1011 * @psoc_ctx: pmo psoc private context
1012 *
1013 * Return: QDF_STATUS_SUCCESS for success or error code
1014 */
1015static
1016QDF_STATUS pmo_core_psoc_resume_target(struct wlan_objmgr_psoc *psoc,
1017 struct pmo_psoc_priv_obj *psoc_ctx)
1018{
1019 QDF_STATUS status = QDF_STATUS_SUCCESS;
1020
1021 PMO_ENTER();
1022 qdf_event_reset(&psoc_ctx->wow.target_resume);
1023
1024 status = pmo_tgt_psoc_send_target_resume_req(psoc);
1025 if (status != QDF_STATUS_SUCCESS) {
1026 status = QDF_STATUS_E_FAILURE;
1027 goto out;
1028 }
1029
1030 status = qdf_wait_single_event(&psoc_ctx->wow.target_resume,
1031 PMO_RESUME_TIMEOUT);
1032 if (status != QDF_STATUS_SUCCESS) {
1033 pmo_fatal("Timeout waiting for resume event from FW");
1034 pmo_fatal("Pending commands %d credits %d",
1035 pmo_tgt_psoc_get_pending_cmnds(psoc),
1036 pmo_tgt_psoc_get_host_credits(psoc));
1037 QDF_BUG(0);
1038 } else {
1039 pmo_debug("Host wakeup received");
1040 }
1041
1042 if (status == QDF_STATUS_SUCCESS)
1043 pmo_tgt_update_target_suspend_flag(psoc, false);
1044out:
1045 PMO_EXIT();
1046
1047 return status;
1048}
1049
1050QDF_STATUS pmo_core_psoc_bus_resume_req(struct wlan_objmgr_psoc *psoc,
1051 enum qdf_suspend_type type)
1052{
1053 struct pmo_psoc_priv_obj *psoc_ctx;
1054 bool wow_mode;
1055 QDF_STATUS status;
1056
1057 PMO_ENTER();
1058 if (!psoc) {
1059 pmo_err("psoc is null");
1060 status = QDF_STATUS_E_NULL_VALUE;
1061 goto out;
1062 }
1063
1064 status = pmo_psoc_get_ref(psoc);
1065 if (status != QDF_STATUS_SUCCESS) {
1066 pmo_err("pmo cannot get the reference out of psoc");
1067 goto out;
1068 }
1069
1070 psoc_ctx = pmo_psoc_get_priv(psoc);
1071 wow_mode = pmo_core_is_wow_enabled(psoc_ctx);
1072 pmo_info("wow mode %d", wow_mode);
1073
1074 pmo_core_update_wow_initial_wake_up(psoc_ctx, false);
1075
1076 if (wow_mode)
1077 status = pmo_core_psoc_disable_wow_in_fw(psoc, psoc_ctx);
1078 else
1079 status = pmo_core_psoc_resume_target(psoc, psoc_ctx);
1080
1081 pmo_psoc_put_ref(psoc);
1082
1083out:
1084 PMO_EXIT();
1085
1086 return status;
1087}
1088
1089void pmo_core_psoc_target_suspend_acknowledge(void *context, bool wow_nack)
1090{
1091 struct pmo_psoc_priv_obj *psoc_ctx;
1092 struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)context;
1093 QDF_STATUS status;
1094
1095 PMO_ENTER();
1096 if (!psoc) {
1097 pmo_err("psoc is null");
1098 goto out;
1099 }
1100
1101 status = pmo_psoc_get_ref(psoc);
1102 if (status != QDF_STATUS_SUCCESS) {
1103 pmo_err("Failed to get psoc reference");
1104 goto out;
1105 }
1106
1107 psoc_ctx = pmo_psoc_get_priv(psoc);
1108
1109 pmo_core_set_wow_nack(psoc_ctx, wow_nack);
1110 qdf_event_set(&psoc_ctx->wow.target_suspend);
1111 if (wow_nack && !pmo_tgt_psoc_get_runtime_pm_in_progress(psoc)) {
1112 qdf_wake_lock_timeout_acquire(&psoc_ctx->wow.wow_wake_lock,
1113 PMO_WAKE_LOCK_TIMEOUT);
1114 }
1115
1116 pmo_psoc_put_ref(psoc);
1117out:
1118 PMO_EXIT();
1119}
1120
1121void pmo_core_psoc_wakeup_host_event_received(struct wlan_objmgr_psoc *psoc)
1122{
1123 struct pmo_psoc_priv_obj *psoc_ctx;
1124
1125 PMO_ENTER();
1126 if (!psoc) {
1127 pmo_err("psoc is null");
1128 goto out;
1129 }
1130
1131 psoc_ctx = pmo_psoc_get_priv(psoc);
1132 qdf_event_set(&psoc_ctx->wow.target_resume);
1133out:
1134 PMO_EXIT();
1135}
1136
1137int pmo_core_psoc_is_target_wake_up_received(struct wlan_objmgr_psoc *psoc)
1138{
1139 struct pmo_psoc_priv_obj *psoc_ctx;
1140 int ret = 0;
1141 QDF_STATUS status;
1142
1143 if (!psoc) {
1144 pmo_err("psoc is NULL");
1145 ret = -EAGAIN;
1146 goto out;
1147 }
1148
1149 status = pmo_psoc_get_ref(psoc);
1150 if (status != QDF_STATUS_SUCCESS) {
1151 pmo_err("Failed to get psoc reference");
1152 ret = -EAGAIN;
1153 goto out;
1154 }
1155
1156 psoc_ctx = pmo_psoc_get_priv(psoc);
1157 if (pmo_core_get_wow_initial_wake_up(psoc_ctx)) {
1158 pmo_err("Target initial wake up received try again");
1159 ret = -EAGAIN;
1160 }
1161
1162 pmo_psoc_put_ref(psoc);
1163out:
1164 PMO_EXIT();
1165
1166 return ret;
1167}
1168
1169
1170int pmo_core_psoc_clear_target_wake_up(struct wlan_objmgr_psoc *psoc)
1171{
1172 struct pmo_psoc_priv_obj *psoc_ctx;
1173 int ret = 0;
1174 QDF_STATUS status;
1175
1176 if (!psoc) {
1177 pmo_err("psoc is NULL");
1178 ret = -EAGAIN;
1179 goto out;
1180 }
1181
1182 status = pmo_psoc_get_ref(psoc);
1183 if (status != QDF_STATUS_SUCCESS) {
1184 pmo_err("Failed to get psoc reference");
1185 ret = -EAGAIN;
1186 goto out;
1187 }
1188
1189 psoc_ctx = pmo_psoc_get_priv(psoc);
1190 pmo_core_update_wow_initial_wake_up(psoc_ctx, false);
1191
1192 pmo_psoc_put_ref(psoc);
1193out:
1194 PMO_EXIT();
1195
1196 return ret;
1197}
1198
1199void pmo_core_psoc_handle_initial_wake_up(void *cb_ctx)
1200{
1201 struct pmo_psoc_priv_obj *psoc_ctx;
1202 struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)cb_ctx;
1203 QDF_STATUS status;
1204
1205 PMO_ENTER();
1206 if (!psoc) {
1207 pmo_err("cb ctx/psoc is null");
1208 goto out;
1209 }
1210
1211 status = pmo_psoc_get_ref(psoc);
1212 if (status != QDF_STATUS_SUCCESS) {
1213 pmo_err("Failed to get psoc reference");
1214 goto out;
1215 }
1216
1217 psoc_ctx = pmo_psoc_get_priv(psoc);
1218 pmo_core_update_wow_initial_wake_up(psoc_ctx, true);
1219
1220 pmo_psoc_put_ref(psoc);
1221
1222out:
1223 PMO_EXIT();
1224}
1225