diff mbox

[RFT,3/6] wlcore: Add support for runtime PM

Message ID 20180529180605.73622-4-tony@atomide.com (mailing list archive)
State RFC
Delegated to: Kalle Valo
Headers show

Commit Message

Tony Lindgren May 29, 2018, 6:06 p.m. UTC
We can update wlcore to use PM runtime by adding functions for
wlcore_runtime_suspend() and wlcore_runtime_resume() and replacing
calls to wl1271_ps_elp_wakeup() and wl1271_ps_elp_sleep() with calls
to pm_runtime_get_sync() and pm_runtime_put().

Note that the new wlcore_runtime_suspend() and wlcore_runtime_resume()
functions are based on simplified versions of wl1271_ps_elp_sleep() and
wl1271_ps_elp_wakeup().

We don't want to use the old functions as we can now take advantage of
the runtime PM usage count. And we don't need the old elp_work at all.
And we can also remove WL1271_FLAG_ELP_REQUESTED that is no longer needed.

Pretty much the only place where we are not just converting the existing
functions is wl1271_op_suspend() where we add pm_runtime_put_noidle()
to keep the calls paired.

As the next step is to implement runtime PM autosuspend, let's not add
wrapper functions for the generic runtime PM calls. We would be getting
rid of any wrapper functions anyways.

After autoidle we should be able to start using Linux generic wakeirqs
for the padconf interrupt.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/net/wireless/ti/wl18xx/debugfs.c    |  26 +-
 drivers/net/wireless/ti/wlcore/acx.c        |   1 -
 drivers/net/wireless/ti/wlcore/cmd.c        |  11 +-
 drivers/net/wireless/ti/wlcore/debugfs.c    |  79 ++--
 drivers/net/wireless/ti/wlcore/main.c       | 410 ++++++++++++++------
 drivers/net/wireless/ti/wlcore/ps.c         | 146 -------
 drivers/net/wireless/ti/wlcore/ps.h         |   3 -
 drivers/net/wireless/ti/wlcore/scan.c       |  10 +-
 drivers/net/wireless/ti/wlcore/sysfs.c      |  12 +-
 drivers/net/wireless/ti/wlcore/testmode.c   |  18 +-
 drivers/net/wireless/ti/wlcore/tx.c         |   9 +-
 drivers/net/wireless/ti/wlcore/vendor_cmd.c |  27 +-
 drivers/net/wireless/ti/wlcore/wlcore.h     |   1 -
 drivers/net/wireless/ti/wlcore/wlcore_i.h   |   1 -
 14 files changed, 416 insertions(+), 338 deletions(-)

Comments

Tony Lindgren May 31, 2018, 5:14 p.m. UTC | #1
* Tony Lindgren <tony@atomide.com> [180529 18:09]:
> --- a/drivers/net/wireless/ti/wlcore/main.c
> +++ b/drivers/net/wireless/ti/wlcore/main.c
...
> +static int __maybe_unused wlcore_runtime_resume(struct device *dev)
> +{
> +	struct wl1271 *wl = dev_get_drvdata(dev);
> +	DECLARE_COMPLETION_ONSTACK(compl);
> +	unsigned long flags;
> +	int ret;
> +	unsigned long start_time = jiffies;
> +	bool pending = false;
> +
> +	/* Nothing to do if no ELP mode requested */
> +	if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
> +		return 0;
> +
> +	wl1271_debug(DEBUG_PSM, "waking up chip from elp");
> +
> +	spin_lock_irqsave(&wl->wl_lock, flags);
> +	if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
> +		pending = true;
> +	else
> +		wl->elp_compl = &compl;
> +	spin_unlock_irqrestore(&wl->wl_lock, flags);
> +
> +	ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
> +	if (ret < 0) {
> +		wl12xx_queue_recovery_work(wl);
> +		goto err;
> +	}
> +
> +	if (!pending) {
> +		ret = wait_for_completion_timeout(&compl,
> +			msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
> +		if (ret == 0) {
> +			wl1271_error("ELP wakeup timeout!");
> +			wl12xx_queue_recovery_work(wl);
> +
> +			/* Return no error for runtime PM for recovery */
> +			return 0;

I noticed returning here and not clearing WL1271_FLAG_IN_ELP can
produce errors. But I think it's unsafe to clear WL1271_FLAG_IN_ELP
if wlcore did not wake. So I suggest we return early here to see the
errors so we can fix the issues. I'll post one more patch that fixes
the init time "ELP wakeup timeout!" issues for me.

Regards,

Tony
Reizer, Eyal June 3, 2018, 6:04 a.m. UTC | #2
> 
> * Tony Lindgren <tony@atomide.com> [180529 18:09]:
> > --- a/drivers/net/wireless/ti/wlcore/main.c
> > +++ b/drivers/net/wireless/ti/wlcore/main.c
> ...
> > +static int __maybe_unused wlcore_runtime_resume(struct device *dev)
> > +{
> > +	struct wl1271 *wl = dev_get_drvdata(dev);
> > +	DECLARE_COMPLETION_ONSTACK(compl);
> > +	unsigned long flags;
> > +	int ret;
> > +	unsigned long start_time = jiffies;
> > +	bool pending = false;
> > +
> > +	/* Nothing to do if no ELP mode requested */
> > +	if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
> > +		return 0;
> > +
> > +	wl1271_debug(DEBUG_PSM, "waking up chip from elp");
> > +
> > +	spin_lock_irqsave(&wl->wl_lock, flags);
> > +	if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
> > +		pending = true;
> > +	else
> > +		wl->elp_compl = &compl;
> > +	spin_unlock_irqrestore(&wl->wl_lock, flags);
> > +
> > +	ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG,
> ELPCTRL_WAKE_UP);
> > +	if (ret < 0) {
> > +		wl12xx_queue_recovery_work(wl);
> > +		goto err;
> > +	}
> > +
> > +	if (!pending) {
> > +		ret = wait_for_completion_timeout(&compl,
> > +			msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
> > +		if (ret == 0) {
> > +			wl1271_error("ELP wakeup timeout!");
> > +			wl12xx_queue_recovery_work(wl);
> > +
> > +			/* Return no error for runtime PM for recovery */
> > +			return 0;
> 
> I noticed returning here and not clearing WL1271_FLAG_IN_ELP can
> produce errors. But I think it's unsafe to clear WL1271_FLAG_IN_ELP
> if wlcore did not wake. So I suggest we return early here to see the
> errors so we can fix the issues. I'll post one more patch that fixes
> the init time "ELP wakeup timeout!" issues for me.
> 

I have noticed the following recovery a couple of times on my setup when the board was 
just sitting for a long time with just pings
It starts with a firmware recovery started from the interrupt handler but the recovery fails 
leaving the sdio stuck.
At this stage the only way to get out of it is unload/load of the driver modules.
Have you seen this on your side as well?

64 bytes from 192.168.100.1: seq=32772 ttl=64 time=9.644 ms
64 bytes from 192.168.100.1: seq=32773 ttl=64 time=9.572 ms
64 bytes from 192.168.100.1: seq=32774 ttl=64 time=10.974 ms
64 bytes from 192.168.100.1: seq=32775 ttl=64 time=9.618 ms
[127899.040526] wlcore: ERROR SW watchdog interrupt received! starting recovery.
[127899.047922] ------------[ cut here ]------------
[127899.053009] WARNING: CPU: 0 PID: 20135 at drivers/net/wireless/ti/wlcore/main.c:806 wl12xx_queue_recovery_work+0x64/0x6c [wlcore]
[127899.064899] Modules linked in: wlcore_sdio wl18xx wlcore mac80211 cfg80211 ctr aes_arm_bs crypto_simd cryptd ccm usb_f_acm u_serial arc4 pru_rproc pruss_intc usb_f_ecm pruss musb_dsps musb_hdrc phy_am335x usbcore phy_generic phy_am335x_control xfrm_user xfrm4_tunnel ipcomp xfrm_ipcomp esp4 ah4 g_multi usb_f_mass_storage usb_f_rndis af_key u_ether xfrm_algo libcomposite udc_core usb_common bluetooth ecdh_generic snd_soc_simple_card snd_soc_simple_card_utils pm33xx wkup_m3_rproc wkup_m3_ipc remoteproc omap_aes_driver crypto_engine omap_crypto omap_sham ti_emif_sram pruss_soc_bus musb_am335x rtc_omap omap_wdt sch_fq_codel [last unloaded: cfg80211]
[127899.122874] CPU: 0 PID: 20135 Comm: irq/65-wl18xx Tainted: G        W       4.14.40-01413-g9541ff6-dirty #107
[127899.132988] Hardware name: Generic AM33XX (Flattened Device Tree)
[127899.139247] Backtrace:
[127899.141866] [<c010baf8>] (dump_backtrace) from [<c010bd5c>] (show_stack+0x18/0x1c)
[127899.149636]  r6:00000000 r5:bf4e25fc r4:00000000 r3:00000000
[127899.155493] [<c010bd44>] (show_stack) from [<c0805dd0>] (dump_stack+0x20/0x28)
[127899.162876] [<c0805db0>] (dump_stack) from [<c01287bc>] (__warn+0xdc/0x104)
[127899.170032] [<c01286e0>] (__warn) from [<c012880c>] (warn_slowpath_null+0x28/0x30)
[127899.177795]  r10:c0d4ea79 r8:c0169590 r7:db276658 r6:d6abaec8 r5:d6abad38 r4:d6abad00
[127899.185989] [<c01287e4>] (warn_slowpath_null) from [<bf4cd4d0>] (wl12xx_queue_recovery_work+0x64/0x6c [wlcore])
[127899.196605] [<bf4cd46c>] (wl12xx_queue_recovery_work [wlcore]) from [<bf4cda80>] (wlcore_irq+0x21c/0x234 [wlcore])
[127899.207161]  r4:d6abad00 r3:00000001
[127899.211036] [<bf4cd864>] (wlcore_irq [wlcore]) from [<c01695b4>] (irq_thread_fn+0x24/0x3c)
[127899.219498]  r7:db276658 r6:db276600 r5:00000001 r4:d6818ec0
[127899.225341] [<c0169590>] (irq_thread_fn) from [<c0169260>] (irq_thread+0x10c/0x1ec)
[127899.233143]  r6:db276600 r5:00000001 r4:d6818ec0 r3:db361200
[127899.238997] [<c0169154>] (irq_thread) from [<c014526c>] (kthread+0x11c/0x154)
[127899.246324]  r10:c0169154 r8:d6818ec0 r7:d6818bd8 r6:d6818b80 r5:00000000 r4:d6818bc0
[127899.254313] [<c0145150>] (kthread) from [<c0107f68>] (ret_from_fork+0x14/0x2c)
[127899.261724]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0145150
[127899.269728]  r4:d6818b80 r3:ffffffff
[127899.273422] ---[ end trace 5ef4c2e6abb6b669 ]---
[127899.285612] wlcore: Hardware recovery in progress. FW ver: Rev 8.9.0.6.76
[127899.303137] wlcore: pc: 0xba1c, hint_sts: 0x00000000 count: 1
[127899.315757] wlcore: down
[127899.318420] wlcore: down
[127899.326230] ieee80211 phy0: Hardware restart was requested
[127901.063547] ------------[ cut here ]------------
[127901.068548] WARNING: CPU: 0 PID: 26417 at drivers/net/wireless/ti/wlcore/sdio.c:145 wl12xx_sdio_raw_write+0xb4/0x138 [wlcore_sdio]
[127901.085327] Modules linked in: wlcore_sdio wl18xx wlcore mac80211 cfg80211 ctr aes_arm_bs crypto_simd cryptd ccm usb_f_acm u_serial arc4 pru_rproc pruss_intc usb_f_ecm pruss musb_dsps musb_hdrc phy_am335x usbcore phy_generic phy_am335x_control xfrm_user xfrm4_tunnel ipcomp xfrm_ipcomp esp4 ah4 g_multi usb_f_mass_storage usb_f_rndis af_key u_ether xfrm_algo libcomposite udc_core usb_common bluetooth ecdh_generic snd_soc_simple_card snd_soc_simple_card_utils pm33xx wkup_m3_rproc wkup_m3_ipc remoteproc omap_aes_driver crypto_engine omap_crypto omap_sham ti_emif_sram pruss_soc_bus musb_am335x rtc_omap omap_wdt sch_fq_codel [last unloaded: cfg80211]
[127901.160115] CPU: 0 PID: 26417 Comm: kworker/0:4 Tainted: G        W       4.14.40-01413-g9541ff6-dirty #107
[127901.174489] Hardware name: Generic AM33XX (Flattened Device Tree)
[127901.185357] Workqueue: events_freezable ieee80211_restart_work [mac80211]
[127901.192628] Backtrace:
[127901.200496] [<c010baf8>] (dump_backtrace) from [<c010bd5c>] (show_stack+0x18/0x1c)
[127901.214303]  r6:00000000 r5:bf073ad8 r4:00000000 r3:00000000
[127901.224251] [<c010bd44>] (show_stack) from [<c0805dd0>] (dump_stack+0x20/0x28)
[127901.236220] [<c0805db0>] (dump_stack) from [<c01287bc>] (__warn+0xdc/0x104)
[127901.243355] [<c01286e0>] (__warn) from [<c012880c>] (warn_slowpath_null+0x28/0x30)
[127901.255388]  r10:00000000 r8:00004000 r7:d6838000 r6:db259c10 r5:00000000 r4:dc58ca00
[127901.270249] [<c01287e4>] (warn_slowpath_null) from [<bf0722fc>] (wl12xx_sdio_raw_write+0xb4/0x138 [wlcore_sdio])
[127901.284893] [<bf072248>] (wl12xx_sdio_raw_write [wlcore_sdio]) from [<bf4db23c>] (wlcore_boot_upload_firmware+0x1f0/0x444 [wlcore])
[127901.301116]  r10:e221f65c r8:d6838000 r7:bf4e9c40 r6:00014000 r5:00000000 r4:80900000
[127901.313657] [<bf4db04c>] (wlcore_boot_upload_firmware [wlcore]) from [<bf37205c>] (wl18xx_boot+0x974/0xd14 [wl18xx])
[127901.328804]  r10:00000000 r9:00000003 r8:db6b6bc8 r7:c0b134fc r6:bf4e9c40 r5:00000000
[127901.341001]  r4:d6abad00
[127901.344099] [<bf3716e8>] (wl18xx_boot [wl18xx]) from [<bf4cd18c>] (wl1271_op_add_interface+0x670/0x950 [wlcore])
[127901.359852]  r10:00000000 r8:db6b6bc8 r7:bf4e9c40 r6:d6abad38 r5:d6abad00 r4:00000000
[127901.374898] [<bf4ccb1c>] (wl1271_op_add_interface [wlcore]) from [<bf45cfe0>] (drv_add_interface+0x34/0x80 [mac80211])
[127901.390201]  r10:d6abaa00 r9:c0d0ea60 r8:00000000 r7:d6aba420 r6:d6aba420 r5:db6b64e0
[127901.402221]  r4:db6b64e0
[127901.406026] [<bf45cfac>] (drv_add_interface [mac80211]) from [<bf48bfe8>] (ieee80211_reconfig+0x164/0xc0c [mac80211])
[127901.425079]  r4:d6aba420 r3:00000001
[127901.433671] [<bf48be84>] (ieee80211_reconfig [mac80211]) from [<bf45b354>] (ieee80211_restart_work+0x90/0xbc [mac80211])
[127901.449257]  r10:00000000 r9:c0d0ea60 r8:00000000 r7:d6aba420 r6:d6aba420 r5:d6abaa00
[127901.461411]  r4:d6abaa00
[127901.464785] [<bf45b2c4>] (ieee80211_restart_work [mac80211]) from [<c013f128>] (process_one_work+0x12c/0x374)
[127901.480386]  r7:dcb39400 r6:c0d0ea60 r5:d6abac28 r4:db74e600
[127901.492467] [<c013effc>] (process_one_work) from [<c013f570>] (worker_thread+0x1c0/0x5b8)
[127901.504786]  r10:00000008 r9:c0d0ea60 r8:c0d16380 r7:db74e618 r6:c0d0ea74 r5:c0d0ea60
[127901.517204]  r4:db74e600
[127901.519918] [<c013f3b0>] (worker_thread) from [<c014526c>] (kthread+0x11c/0x154)
[127901.532463]  r10:c013f3b0 r9:d68fdeb4 r8:db74e600 r7:db4b2058 r6:db6ec4c0 r5:00000000
[127901.546911]  r4:db4b2040 r3:00000000
[127901.550678] [<c0145150>] (kthread) from [<c0107f68>] (ret_from_fork+0x14/0x2c)
[127901.562972]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0145150
[127901.577275]  r4:db6ec4c0 r3:ffffffff
[127901.580991] ---[ end trace 5ef4c2e6abb6b66a ]---
[127901.590827] wl1271_sdio mmc1:0001:2: sdio write failed (-110)
[127901.895777] ------------[ cut here ]------------
[127901.900624] WARNING: CPU: 0 PID: 26417 at drivers/net/wireless/ti/wlcore/sdio.c:145 wl12xx_sdio_raw_write+0xb4/0x138 [wlcore_sdio]
[127901.920440] Modules linked in: wlcore_sdio wl18xx wlcore mac80211 cfg80211 ctr aes_arm_bs crypto_simd cryptd ccm usb_f_acm u_serial arc4 pru_rproc pruss_intc usb_f_ecm pruss musb_dsps musb_hdrc phy_am335x usbcore phy_generic phy_am335x_

Best Regards,
Eyal
Tony Lindgren June 5, 2018, 4:20 a.m. UTC | #3
* Reizer, Eyal <eyalr@ti.com> [180603 06:07]:
> I have noticed the following recovery a couple of times on my setup when the board was 
> just sitting for a long time with just pings
> It starts with a firmware recovery started from the interrupt handler but the recovery fails 

Sounds like the recovery needs some more work :)

> leaving the sdio stuck.
> At this stage the only way to get out of it is unload/load of the driver modules.
> Have you seen this on your side as well?

Hmm I don't think I've seen this one yet.

> 64 bytes from 192.168.100.1: seq=32772 ttl=64 time=9.644 ms
> 64 bytes from 192.168.100.1: seq=32773 ttl=64 time=9.572 ms
> 64 bytes from 192.168.100.1: seq=32774 ttl=64 time=10.974 ms
> 64 bytes from 192.168.100.1: seq=32775 ttl=64 time=9.618 ms
> [127899.040526] wlcore: ERROR SW watchdog interrupt received! starting recovery.

Do you know what does the SW watchdog means here? Does it mean the
interrupt did not get delivered to wlcore? Or a spurious IRQ to wlcore?
Or a timeout waiting for the ELP wake interrupt?

Regards,

Tony
Tony Lindgren June 5, 2018, 10:44 a.m. UTC | #4
* Tony Lindgren <tony@atomide.com> [180605 04:22]:
> * Reizer, Eyal <eyalr@ti.com> [180603 06:07]:
> > I have noticed the following recovery a couple of times on my setup when the board was 
> > just sitting for a long time with just pings
> > It starts with a firmware recovery started from the interrupt handler but the recovery fails 
> 
> Sounds like the recovery needs some more work :)
> 
> > leaving the sdio stuck.
> > At this stage the only way to get out of it is unload/load of the driver modules.
> > Have you seen this on your side as well?
> 
> Hmm I don't think I've seen this one yet.
> 
> > 64 bytes from 192.168.100.1: seq=32772 ttl=64 time=9.644 ms
> > 64 bytes from 192.168.100.1: seq=32773 ttl=64 time=9.572 ms
> > 64 bytes from 192.168.100.1: seq=32774 ttl=64 time=10.974 ms
> > 64 bytes from 192.168.100.1: seq=32775 ttl=64 time=9.618 ms
> > [127899.040526] wlcore: ERROR SW watchdog interrupt received! starting recovery.
> 
> Do you know what does the SW watchdog means here? Does it mean the
> interrupt did not get delivered to wlcore? Or a spurious IRQ to wlcore?
> Or a timeout waiting for the ELP wake interrupt?

Also, can you check if patch "[RFT 7/6] wlcore: Make sure firmware is initialized
in wl1271_op_add_interface()" already fixes this issue if you did not have it
already applied?

Regards,

Tony
Reizer, Eyal June 5, 2018, 10:49 a.m. UTC | #5
> 
> * Tony Lindgren <tony@atomide.com> [180605 04:22]:
> > * Reizer, Eyal <eyalr@ti.com> [180603 06:07]:
> > > I have noticed the following recovery a couple of times on my setup
> when the board was
> > > just sitting for a long time with just pings
> > > It starts with a firmware recovery started from the interrupt handler but
> the recovery fails
> >
> > Sounds like the recovery needs some more work :)
> >
> > > leaving the sdio stuck.
> > > At this stage the only way to get out of it is unload/load of the driver
> modules.
> > > Have you seen this on your side as well?
> >
> > Hmm I don't think I've seen this one yet.
> >
> > > 64 bytes from 192.168.100.1: seq=32772 ttl=64 time=9.644 ms
> > > 64 bytes from 192.168.100.1: seq=32773 ttl=64 time=9.572 ms
> > > 64 bytes from 192.168.100.1: seq=32774 ttl=64 time=10.974 ms
> > > 64 bytes from 192.168.100.1: seq=32775 ttl=64 time=9.618 ms
> > > [127899.040526] wlcore: ERROR SW watchdog interrupt received! starting
> recovery.
> >
> > Do you know what does the SW watchdog means here? Does it mean the
> > interrupt did not get delivered to wlcore? Or a spurious IRQ to wlcore?
> > Or a timeout waiting for the ELP wake interrupt?
> 
> Also, can you check if patch "[RFT 7/6] wlcore: Make sure firmware is
> initialized
> in wl1271_op_add_interface()" already fixes this issue if you did not have it
> already applied?
> 
Sure will do. The firmware that showed this issue has some extra debugging info
Enabled in it and this may have been the cause of the recovery (Infernal logging error).
The fact that it didn't recover was the issue itself. I am now running with the latest's 
firmware 8.9.0.0.78 and will see if I can still replicate it.
If I see it, I will try applying patch 7 and see if it helps. Right now I don't yet have
It applied.

Best Regard,
Eyal
Reizer, Eyal June 6, 2018, 12:20 p.m. UTC | #6
> > * Tony Lindgren <tony@atomide.com> [180605 04:22]:
> > > * Reizer, Eyal <eyalr@ti.com> [180603 06:07]:
> > > > I have noticed the following recovery a couple of times on my setup
> > when the board was
> > > > just sitting for a long time with just pings
> > > > It starts with a firmware recovery started from the interrupt handler
> but
> > the recovery fails
> > >
> > > Sounds like the recovery needs some more work :)
> > >
> > > > leaving the sdio stuck.
> > > > At this stage the only way to get out of it is unload/load of the driver
> > modules.
> > > > Have you seen this on your side as well?
> > >
> > > Hmm I don't think I've seen this one yet.
> > >
> > > > 64 bytes from 192.168.100.1: seq=32772 ttl=64 time=9.644 ms
> > > > 64 bytes from 192.168.100.1: seq=32773 ttl=64 time=9.572 ms
> > > > 64 bytes from 192.168.100.1: seq=32774 ttl=64 time=10.974 ms
> > > > 64 bytes from 192.168.100.1: seq=32775 ttl=64 time=9.618 ms
> > > > [127899.040526] wlcore: ERROR SW watchdog interrupt received!
> starting
> > recovery.
> > >
> > > Do you know what does the SW watchdog means here? Does it mean the
> > > interrupt did not get delivered to wlcore? Or a spurious IRQ to wlcore?
> > > Or a timeout waiting for the ELP wake interrupt?
> >
> > Also, can you check if patch "[RFT 7/6] wlcore: Make sure firmware is
> > initialized
> > in wl1271_op_add_interface()" already fixes this issue if you did not have it
> > already applied?
> >
> Sure will do. The firmware that showed this issue has some extra debugging
> info
> Enabled in it and this may have been the cause of the recovery (Infernal
> logging error).
> The fact that it didn't recover was the issue itself. I am now running with the
> latest's
> firmware 8.9.0.0.78 and will see if I can still replicate it.
> If I see it, I will try applying patch 7 and see if it helps. Right now I don't yet
> have
> It applied.
> 

Latest wl18xx firmware was running fine for two days without the crash, so it was indeed just a logging issue in this case.
However, trying a wl1281 module and enabling PLT mode it boots ok but after a couple of seconds the below crash is seen.
Seems like a similar crash to the one I have seen before,right?
I do have patch 7/6 applied now.

sh-4.4# calibrator wlan0 plt power_mode on
[   57.198492] wlcore: power up
[   57.757871] wlcore: firmware booted in PLT mode PLT_ON (PLT 7.3.10.2.142)
sh-4.4#
sh-4.4#
sh-4.4# ca[   86.485020] ------------[ cut here ]------------
[   86.490334] WARNING: CPU: 0 PID: 502 at drivers/net/wireless/ti/wlcore/main.c:806 wl12xx_queue_recovery_work+0x64/0x6c [wlcore]
[   86.502047] Modules linked in: arc4 pru_rproc wl12xx pruss_intc wlcore mac80211 cfg80211 pruss ti_am335x_tsc ti_am335x_adc musb_dsps musb_hdrc udc_core usbcore phy_am335x phy_generic phy_am335x_control usb_common pm33xx wkup_m3_ipc snd_soc_simple_card snd_soc_simple_card_utils wkup_m3_rproc remoteproc omap_aes_driver crypto_engine omap_sham omap_crypto ti_emif_sram pruss_soc_bus snd_soc_tlv320aic3x wlcore_sdio matrix_keypad rtc_omap ti_am335x_tscadc omap_wdt matrix_keymap musb_am335x sch_fq_codel
[   86.546686] CPU: 0 PID: 502 Comm: irq/71-wl12xx Not tainted 4.14.40-01415-g47241db-dirty #119
[   86.555312] Hardware name: Generic AM33XX (Flattened Device Tree)
[   86.561540] Backtrace:
[   86.564119] [<c010baf8>] (dump_backtrace) from [<c010bd5c>] (show_stack+0x18/0x1c)
[   86.571838]  r6:00000000 r5:bf30a5fc r4:00000000 r3:00000000
[   86.577661] [<c010bd44>] (show_stack) from [<c0805dd0>] (dump_stack+0x20/0x28)
[   86.584994] [<c0805db0>] (dump_stack) from [<c01287bc>] (__warn+0xdc/0x104)
[   86.592121] [<c01286e0>] (__warn) from [<c012880c>] (warn_slowpath_null+0x28/0x30)
[   86.599838]  r10:c0d4ea79 r8:c0169590 r7:db0f1558 r6:dc716ec8 r5:dc716d38 r4:dc716d00
[   86.608019] [<c01287e4>] (warn_slowpath_null) from [<bf2f5468>] (wl12xx_queue_recovery_work+0x64/0x6c [wlcore])
[   86.618630] [<bf2f5404>] (wl12xx_queue_recovery_work [wlcore]) from [<bf2f5a18>] (wlcore_irq+0x21c/0x234 [wlcore])
[   86.629157]  r4:dc716d00 r3:db164010
[   86.633009] [<bf2f57fc>] (wlcore_irq [wlcore]) from [<c01695b4>] (irq_thread_fn+0x24/0x3c)
[   86.641441]  r7:db0f1558 r6:db0f1500 r5:00000001 r4:db1b8680
[   86.647236] [<c0169590>] (irq_thread_fn) from [<c0169260>] (irq_thread+0x10c/0x1ec)
[   86.655044]  r6:db0f1500 r5:00000001 r4:db1b8680 r3:db623600
[   86.660875] [<c0169154>] (irq_thread) from [<c014526c>] (kthread+0x11c/0x154)
[   86.668148]  r10:c0169154 r8:db1b8680 r7:db1b8958 r6:db1b8600 r5:00000000 r4:db1b8940
[   86.676104] [<c0145150>] (kthread) from [<c0107f68>] (ret_from_fork+0x14/0x2c)
[   86.683479]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0145150
[   86.691451]  r4:db1b8600 r3:ffffffff
[   86.695099] ---[ end trace dea7def5777109c9 ]---

BR,
Eyal
Reizer, Eyal June 14, 2018, 8:36 a.m. UTC | #7
>> However, trying a wl1281 module and enabling PLT mode it boots ok but after a couple of seconds the below crash is seen.
>> Seems like a similar crash to the one I have seen before,right?
>
>Sorry for the delay, only today had enough time to figure
>this one out, see below.
>
>> sh-4.4# calibrator wlan0 plt power_mode on
>> [   57.198492] wlcore: power up
>> [   57.757871] wlcore: firmware booted in PLT mode PLT_ON (PLT 7.3.10.2.142)
>> sh-4.4#
>> sh-4.4#
>> sh-4.4# ca[   86.485020] ------------[ cut here ]------------
>> [   86.490334] WARNING: CPU: 0 PID: 502 at drivers/net/wireless/ti/wlcore/main.c:806
>
>This happens on runtime_suspend() where we are already in PLT
>and then that error gets stored and then next pm_runtime_get()
>returns -EINVAL. The patch below should fix it. I'll fold it
i>nto the runtime PM related patch assuming it works for you.
>
>Regards,
>
>Tony
>
>8< -------
>diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
>--- a/drivers/net/wireless/ti/wlcore/main.c
>+++ b/drivers/net/wireless/ti/wlcore/main.c
>@@ -6677,7 +6677,7 @@ static int __maybe_unused wlcore_runtime_suspend(struct device *dev)
> 
> 	/* We do not enter elp sleep in PLT mode */
> 	if (wl->plt)
>-		return -EINVAL;
>+		return 0;
> 
> 	/* Nothing to do if no ELP mode requested */
> 	if (wl->sleep_auth != WL1271_PSM_ELP)

Even with this change I still see issues with a wl1281 module plugged in.
It take a few seconds for the crash to happen once you turn plt on.
Log below.
Do you see this on your wl12xx based platform?

sh-4.4#
sh-4.4# calibrator wlan0 plt power_mode on
[  231.105877] wlcore: power up
[  231.667604] wlcore: firmware booted in PLT mode PLT_ON (PLT 7.3.10.2.142)
sh-4.4#
sh-4.4#
sh-4.4# [  236.900817] ------------[ cut here ]------------
[  236.906012] WARNING: CPU: 0 PID: 520 at drivers/net/wireless/ti/wlcore/main.c:806 wl12xx_queue_recovery_work+0x64/0x6c [wlcore]
[  236.917783] Modules linked in: ctr aes_arm_bs crypto_simd cryptd ccm arc4 pru_rproc pruss_intc wl12xx wlcore mac80211 cfg80211 pruss musb_dsps musb_hdrc udc_core usbcore phy_am335x phy_generic usb_common phy_am335x_control ti_am335x_adc ti_am335x_tsc snd_soc_simple_card snd_soc_simple_card_utils pm33xx wkup_m3_ipc wkup_m3_rproc remoteproc omap_aes_driver crypto_engine omap_crypto omap_sham ti_emif_sram pruss_soc_bus wlcore_sdio snd_soc_tlv320aic3x rtc_omap musb_am335x omap_wdt ti_am335x_tscadc matrix_keypad matrix_keymap sch_fq_codel
[  236.965780] CPU: 0 PID: 520 Comm: irq/71-wl12xx Not tainted 4.14.40-01415-g47241db-dirty #123
[  236.974439] Hardware name: Generic AM33XX (Flattened Device Tree)
[  236.980666] Backtrace:
[  236.983250] [<c010baf8>] (dump_backtrace) from [<c010bd5c>] (show_stack+0x18/0x1c)
[  236.990967]  r6:00000000 r5:bf2e25fc r4:00000000 r3:00000000
[  236.996799] [<c010bd44>] (show_stack) from [<c0805dd0>] (dump_stack+0x20/0x28)
[  237.004145] [<c0805db0>] (dump_stack) from [<c01287bc>] (__warn+0xdc/0x104)
[  237.011339] [<c01286e0>] (__warn) from [<c012880c>] (warn_slowpath_null+0x28/0x30)
[  237.019099]  r10:c0d4ea79 r8:c0169590 r7:db1bee58 r6:dc726ec8 r5:dc726d38 r4:dc726d00
[  237.027285] [<c01287e4>] (warn_slowpath_null) from [<bf2cd468>] (wl12xx_queue_recovery_work+0x64/0x6c [wlcore])
[  237.037892] [<bf2cd404>] (wl12xx_queue_recovery_work [wlcore]) from [<bf2cda18>] (wlcore_irq+0x21c/0x234 [wlcore])
[  237.048412]  r4:dc726d00 r3:db2d3810
[  237.052260] [<bf2cd7fc>] (wlcore_irq [wlcore]) from [<c01695b4>] (irq_thread_fn+0x24/0x3c)
[  237.060688]  r7:db1bee58 r6:db1bee00 r5:00000001 r4:db1af3c0
[  237.066481] [<c0169590>] (irq_thread_fn) from [<c0169260>] (irq_thread+0x10c/0x1ec)
[  237.074242]  r6:db1bee00 r5:00000001 r4:db1af3c0 r3:dc73aa00
[  237.080069] [<c0169154>] (irq_thread) from [<c014526c>] (kthread+0x11c/0x154)
[  237.087350]  r10:c0169154 r8:db1af3c0 r7:db1af918 r6:db1af340 r5:00000000 r4:db1af900
[  237.095305] [<c0145150>] (kthread) from [<c0107f68>] (ret_from_fork+0x14/0x2c)
[  237.102673]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0145150
[  237.110646]  r4:db1af340 r3:ffffffff
[  237.114292] ---[ end trace 2c3213a5c8df86b0 ]---
[  237.856438] sched: RT throttling activated
[  238.886549] omap_i2c 44e0b000.i2c: controller timed out
[  242.846592] omap_i2c 44e0b000.i2c: controller timed out

BR,
Eyal
Reizer, Eyal June 14, 2018, 11:29 a.m. UTC | #8
> >This happens on runtime_suspend() where we are already in PLT
> >and then that error gets stored and then next pm_runtime_get()
> >returns -EINVAL. The patch below should fix it. I'll fold it
> i>nto the runtime PM related patch assuming it works for you.
> >
> >Regards,
> >
> >Tony
> >
> >8< -------
> >diff --git a/drivers/net/wireless/ti/wlcore/main.c
> b/drivers/net/wireless/ti/wlcore/main.c
> >--- a/drivers/net/wireless/ti/wlcore/main.c
> >+++ b/drivers/net/wireless/ti/wlcore/main.c
> >@@ -6677,7 +6677,7 @@ static int __maybe_unused
> wlcore_runtime_suspend(struct device *dev)
> >
> > 	/* We do not enter elp sleep in PLT mode */
> > 	if (wl->plt)
> >-		return -EINVAL;
> >+		return 0;
> >
> > 	/* Nothing to do if no ELP mode requested */
> > 	if (wl->sleep_auth != WL1271_PSM_ELP)
> 
> Even with this change I still see issues with a wl1281 module plugged in.
> It take a few seconds for the crash to happen once you turn plt on.
> Log below.
> Do you see this on your wl12xx based platform?
> 
> sh-4.4#
> sh-4.4# calibrator wlan0 plt power_mode on
> [  231.105877] wlcore: power up
> [  231.667604] wlcore: firmware booted in PLT mode PLT_ON (PLT
> 7.3.10.2.142)
> sh-4.4#
> sh-4.4#
> sh-4.4# [  236.900817] ------------[ cut here ]------------
> [  236.906012] WARNING: CPU: 0 PID: 520 at
> drivers/net/wireless/ti/wlcore/main.c:806

Hold on, I might have edited the wrong version of the file (main.c). 
Sorry about that. Working on too many branches/boards... :(
Applied this change again and things look good now with the wl1281 based module.

Best Regards,
Eyal
Tony Lindgren June 15, 2018, 5:04 a.m. UTC | #9
* Reizer, Eyal <eyalr@ti.com> [180614 11:32]:
> Hold on, I might have edited the wrong version of the file (main.c). 
> Sorry about that. Working on too many branches/boards... :(
> Applied this change again and things look good now with the wl1281 based module.

Heh OK :) Sounds like time to post the whole series next week
after -rc1 if no more issues.

Regards,

Tony
diff mbox

Patch

diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c
--- a/drivers/net/wireless/ti/wl18xx/debugfs.c
+++ b/drivers/net/wireless/ti/wl18xx/debugfs.c
@@ -20,6 +20,8 @@ 
  *
  */
 
+#include <linux/pm_runtime.h>
+
 #include "../wlcore/debugfs.h"
 #include "../wlcore/wlcore.h"
 #include "../wlcore/debug.h"
@@ -276,15 +278,17 @@  static ssize_t radar_detection_write(struct file *file,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl18xx_cmd_radar_detection_debug(wl, channel);
 	if (ret < 0)
 		count = ret;
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
@@ -315,15 +319,17 @@  static ssize_t dynamic_fw_traces_write(struct file *file,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl18xx_acx_dynamic_fw_traces(wl);
 	if (ret < 0)
 		count = ret;
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
@@ -374,9 +380,11 @@  static ssize_t radar_debug_mode_write(struct file *file,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif_ap(wl, wlvif) {
 		wlcore_cmd_generic_cfg(wl, wlvif,
@@ -384,7 +392,7 @@  static ssize_t radar_debug_mode_write(struct file *file,
 				       wl->radar_debug_mode, 0);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -31,7 +31,6 @@ 
 #include "wlcore.h"
 #include "debug.h"
 #include "wl12xx_80211.h"
-#include "ps.h"
 #include "hw_ops.h"
 
 int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif,
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -23,6 +23,7 @@ 
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/spi/spi.h>
 #include <linux/etherdevice.h>
 #include <linux/ieee80211.h>
@@ -35,7 +36,6 @@ 
 #include "wl12xx_80211.h"
 #include "cmd.h"
 #include "event.h"
-#include "ps.h"
 #include "tx.h"
 #include "hw_ops.h"
 
@@ -192,9 +192,12 @@  int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
 
 	timeout_time = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT);
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
+
 		return ret;
+	}
 
 	do {
 		if (time_after(jiffies, timeout_time)) {
@@ -227,7 +230,7 @@  int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
 	} while (!event);
 
 out:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 	kfree(events_vector);
 	return ret;
 }
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -26,6 +26,7 @@ 
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 
 #include "wlcore.h"
 #include "debug.h"
@@ -65,9 +66,11 @@  void wl1271_debugfs_update_stats(struct wl1271 *wl)
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	if (!wl->plt &&
 	    time_after(jiffies, wl->stats.fw_stats_update +
@@ -76,7 +79,7 @@  void wl1271_debugfs_update_stats(struct wl1271 *wl)
 		wl->stats.fw_stats_update = jiffies;
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -118,14 +121,17 @@  static void chip_op_handler(struct wl1271 *wl, unsigned long value,
 		return;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
+
 		return;
+	}
 
 	chip_op = arg;
 	chip_op(wl);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 }
 
 
@@ -292,9 +298,11 @@  static ssize_t dynamic_ps_timeout_write(struct file *file,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/* In case we're already in PSM, trigger it again to set new timeout
 	 * immediately without waiting for re-association
@@ -305,7 +313,7 @@  static ssize_t dynamic_ps_timeout_write(struct file *file,
 			wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -359,9 +367,11 @@  static ssize_t forced_ps_write(struct file *file,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/* In case we're already in PSM, trigger it again to switch mode
 	 * immediately without waiting for re-association
@@ -374,7 +384,7 @@  static ssize_t forced_ps_write(struct file *file,
 			wl1271_ps_set_mode(wl, wlvif, ps_mode);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -838,15 +848,17 @@  static ssize_t rx_streaming_interval_write(struct file *file,
 
 	wl->conf.rx_streaming.interval = value;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif_sta(wl, wlvif) {
 		wl1271_recalc_rx_streaming(wl, wlvif);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
@@ -893,15 +905,17 @@  static ssize_t rx_streaming_always_write(struct file *file,
 
 	wl->conf.rx_streaming.always = value;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif_sta(wl, wlvif) {
 		wl1271_recalc_rx_streaming(wl, wlvif);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
@@ -940,15 +954,17 @@  static ssize_t beacon_filtering_write(struct file *file,
 
 	mutex_lock(&wl->mutex);
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
@@ -1019,16 +1035,18 @@  static ssize_t sleep_auth_write(struct file *file,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl1271_acx_sleep_auth(wl, value);
 	if (ret < 0)
 		goto out_sleep;
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return count;
@@ -1083,7 +1101,7 @@  static ssize_t dev_mem_read(struct file *file,
 	 * Don't fail if elp_wakeup returns an error, so the device's memory
 	 * could be read even if the FW crashed
 	 */
-	wl1271_ps_elp_wakeup(wl);
+	pm_runtime_get_sync(wl->dev);
 
 	/* store current partition and switch partition */
 	memcpy(&old_part, &wl->curr_part, sizeof(old_part));
@@ -1102,7 +1120,7 @@  static ssize_t dev_mem_read(struct file *file,
 		goto part_err;
 
 part_err:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 skip_read:
 	mutex_unlock(&wl->mutex);
@@ -1164,7 +1182,7 @@  static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
 	 * Don't fail if elp_wakeup returns an error, so the device's memory
 	 * could be read even if the FW crashed
 	 */
-	wl1271_ps_elp_wakeup(wl);
+	pm_runtime_get_sync(wl->dev);
 
 	/* store current partition and switch partition */
 	memcpy(&old_part, &wl->curr_part, sizeof(old_part));
@@ -1183,7 +1201,7 @@  static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
 		goto part_err;
 
 part_err:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 skip_write:
 	mutex_unlock(&wl->mutex);
@@ -1247,8 +1265,9 @@  static ssize_t fw_logger_write(struct file *file,
 	}
 
 	mutex_lock(&wl->mutex);
-	ret = wl1271_ps_elp_wakeup(wl);
+	ret = pm_runtime_get_sync(wl->dev);
 	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		count = ret;
 		goto out;
 	}
@@ -1257,7 +1276,7 @@  static ssize_t fw_logger_write(struct file *file,
 
 	ret = wl12xx_cmd_config_fwlog(wl);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -26,6 +26,7 @@ 
 #include <linux/vmalloc.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/pm_runtime.h>
 
 #include "wlcore.h"
 #include "debug.h"
@@ -43,6 +44,7 @@ 
 
 #define WL1271_BOOT_RETRIES 3
 #define WL1271_SUSPEND_SLEEP 100
+#define WL1271_WAKEUP_TIMEOUT 500
 
 static char *fwlog_param;
 static int fwlog_mem_blocks = -1;
@@ -153,9 +155,11 @@  static void wl1271_rx_streaming_enable_work(struct work_struct *work)
 	if (!wl->conf.rx_streaming.interval)
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl1271_set_rx_streaming(wl, wlvif, true);
 	if (ret < 0)
@@ -166,7 +170,7 @@  static void wl1271_rx_streaming_enable_work(struct work_struct *work)
 		  jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -183,16 +187,18 @@  static void wl1271_rx_streaming_disable_work(struct work_struct *work)
 	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl1271_set_rx_streaming(wl, wlvif, false);
 	if (ret)
 		goto out_sleep;
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -229,9 +235,11 @@  static void wlcore_rc_update_work(struct work_struct *work)
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	if (ieee80211_vif_is_mesh(vif)) {
 		ret = wl1271_acx_set_ht_capabilities(wl, &wlvif->rc_ht_cap,
@@ -243,7 +251,7 @@  static void wlcore_rc_update_work(struct work_struct *work)
 	}
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -539,15 +547,16 @@  static int wlcore_irq_locked(struct wl1271 *wl)
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	while (!done && loopcount--) {
 		/*
 		 * In order to avoid a race with the hardirq, clear the flag
-		 * before acknowledging the chip. Since the mutex is held,
-		 * wl1271_ps_elp_wakeup cannot be called concurrently.
+		 * before acknowledging the chip.
 		 */
 		clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
 		smp_mb__after_atomic();
@@ -641,7 +650,7 @@  static int wlcore_irq_locked(struct wl1271 *wl)
 			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	return ret;
@@ -817,6 +826,7 @@  size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
 static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 {
 	u32 end_of_log = 0;
+	int error;
 
 	if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED)
 		return;
@@ -828,8 +838,11 @@  static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 	 * Do not send a stop fwlog command if the fw is hanged or if
 	 * dbgpins are used (due to some fw bug).
 	 */
-	if (wl1271_ps_elp_wakeup(wl))
+	error = pm_runtime_get_sync(wl->dev);
+	if (error < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		return;
+	}
 	if (!wl->watchdog_recovery &&
 	    wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
 		wl12xx_cmd_stop_fwlog(wl);
@@ -924,9 +937,11 @@  static void wl1271_recovery_work(struct work_struct *work)
 	if (wl->state == WLCORE_STATE_OFF || wl->plt)
 		goto out_unlock;
 
-	error = wl1271_ps_elp_wakeup(wl);
-	if (error < 0)
+	error = pm_runtime_get_sync(wl->dev);
+	if (error < 0) {
 		wl1271_warning("Enable for recovery failed");
+		pm_runtime_put_noidle(wl->dev);
+	}
 	wlcore_disable_interrupts_nosync(wl);
 
 	if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
@@ -971,7 +986,7 @@  static void wl1271_recovery_work(struct work_struct *work)
 	 */
 	wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out_unlock:
 	wl->watchdog_recovery = false;
@@ -1190,7 +1205,6 @@  int wl1271_plt_stop(struct wl1271 *wl)
 	wl1271_flush_deferred_work(wl);
 	cancel_work_sync(&wl->netstack_work);
 	cancel_work_sync(&wl->recovery_work);
-	cancel_delayed_work_sync(&wl->elp_work);
 	cancel_delayed_work_sync(&wl->tx_watchdog_work);
 
 	mutex_lock(&wl->mutex);
@@ -1740,8 +1754,9 @@  static int __maybe_unused wl1271_op_suspend(struct ieee80211_hw *hw,
 
 	mutex_lock(&wl->mutex);
 
-	ret = wl1271_ps_elp_wakeup(wl);
+	ret = pm_runtime_get_sync(wl->dev);
 	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		mutex_unlock(&wl->mutex);
 		return ret;
 	}
@@ -1771,6 +1786,7 @@  static int __maybe_unused wl1271_op_suspend(struct ieee80211_hw *hw,
 		goto out_sleep;
 
 out_sleep:
+	pm_runtime_put_noidle(wl->dev);
 	mutex_unlock(&wl->mutex);
 
 	if (ret < 0) {
@@ -1795,7 +1811,6 @@  static int __maybe_unused wl1271_op_suspend(struct ieee80211_hw *hw,
 
 	wlcore_enable_interrupts(wl);
 	flush_work(&wl->tx_work);
-	flush_delayed_work(&wl->elp_work);
 
 	/*
 	 * Cancel the watchdog even if above tx_flush failed. We will detect
@@ -1863,9 +1878,11 @@  static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
 		goto out_sleep;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		if (wlcore_is_p2p_mgmt(wlvif))
@@ -1884,7 +1901,7 @@  static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
 		goto out_sleep;
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	wl->wow_enabled = false;
@@ -1951,7 +1968,6 @@  static void wlcore_op_stop_locked(struct wl1271 *wl)
 	cancel_delayed_work_sync(&wl->scan_complete_work);
 	cancel_work_sync(&wl->netstack_work);
 	cancel_work_sync(&wl->tx_work);
-	cancel_delayed_work_sync(&wl->elp_work);
 	cancel_delayed_work_sync(&wl->tx_watchdog_work);
 
 	/* let's notify MAC80211 about the remaining pending TX frames */
@@ -2066,13 +2082,15 @@  static void wlcore_channel_switch_work(struct work_struct *work)
 	vif = wl12xx_wlvif_to_vif(wlvif);
 	ieee80211_chswitch_done(vif, false);
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_cmd_stop_channel_switch(wl, wlvif);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -2134,14 +2152,16 @@  static void wlcore_pending_auth_complete_work(struct work_struct *work)
 	if (!time_after(time_spare, wlvif->pending_auth_reply_time))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/* cancel the ROC if active */
 	wlcore_update_inconn_sta(wl, wlvif, NULL, false);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -2543,9 +2563,11 @@  static int wl1271_op_add_interface(struct ieee80211_hw *hw,
 	wl12xx_get_vif_count(hw, vif, &vif_count);
 
 	mutex_lock(&wl->mutex);
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out_unlock;
+	}
 
 	/*
 	 * in some very corner case HW recovery scenarios its possible to
@@ -2628,7 +2650,7 @@  static int wl1271_op_add_interface(struct ieee80211_hw *hw,
 	else
 		wl->sta_count++;
 out:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out_unlock:
 	mutex_unlock(&wl->mutex);
 
@@ -2683,9 +2705,11 @@  static void __wl1271_op_remove_interface(struct wl1271 *wl,
 
 	if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
 		/* disable active roles */
-		ret = wl1271_ps_elp_wakeup(wl);
-		if (ret < 0)
+		ret = pm_runtime_get_sync(wl->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(wl->dev);
 			goto deinit;
+		}
 
 		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
 		    wlvif->bss_type == BSS_TYPE_IBSS) {
@@ -2703,7 +2727,7 @@  static void __wl1271_op_remove_interface(struct wl1271 *wl,
 				goto deinit;
 		}
 
-		wl1271_ps_elp_sleep(wl);
+		pm_runtime_put(wl->dev);
 	}
 deinit:
 	wl12xx_tx_reset_wlvif(wl, wlvif);
@@ -3127,9 +3151,11 @@  static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/* configure each interface */
 	wl12xx_for_each_wlvif(wl, wlvif) {
@@ -3139,7 +3165,7 @@  static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 	}
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -3208,9 +3234,11 @@  static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		if (wlcore_is_p2p_mgmt(wlvif))
@@ -3253,7 +3281,7 @@  static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
 	 */
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -3460,13 +3488,15 @@  static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 		goto out_wake_queues;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out_wake_queues;
+	}
 
 	ret = wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out_wake_queues:
 	if (might_change_spare)
@@ -3606,9 +3636,11 @@  static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
 		goto out_unlock;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out_unlock;
+	}
 
 	wlvif->default_key = key_idx;
 
@@ -3622,7 +3654,7 @@  static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
 	}
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out_unlock:
 	mutex_unlock(&wl->mutex);
@@ -3640,7 +3672,7 @@  void wlcore_regdomain_config(struct wl1271 *wl)
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
+	ret = pm_runtime_get_sync(wl->dev);
 	if (ret < 0)
 		goto out;
 
@@ -3650,7 +3682,7 @@  void wlcore_regdomain_config(struct wl1271 *wl)
 		goto out;
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -3684,9 +3716,11 @@  static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/* fail if there is any role in ROC */
 	if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
@@ -3697,7 +3731,7 @@  static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
 
 	ret = wlcore_scan(hw->priv, vif, ssid, len, req);
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -3724,9 +3758,11 @@  static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
 	if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
 		ret = wl->ops->scan_stop(wl, wlvif);
@@ -3747,7 +3783,7 @@  static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
 	ieee80211_scan_completed(wl->hw, &info);
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -3772,9 +3808,11 @@  static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
 	if (ret < 0)
@@ -3783,7 +3821,7 @@  static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
 	wl->sched_vif = wlvif;
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return ret;
@@ -3803,13 +3841,15 @@  static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl->ops->sched_scan_stop(wl, wlvif);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -3828,15 +3868,17 @@  static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl1271_acx_frag_threshold(wl, value);
 	if (ret < 0)
 		wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -3857,16 +3899,18 @@  static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		ret = wl1271_acx_rts_threshold(wl, wlvif, value);
 		if (ret < 0)
 			wl1271_warning("set rts threshold failed: %d", ret);
 	}
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -4613,9 +4657,11 @@  static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
 	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	if ((changed & BSS_CHANGED_TXPOWER) &&
 	    bss_conf->txpower != wlvif->power_level) {
@@ -4632,7 +4678,7 @@  static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
 	else
 		wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -4671,9 +4717,11 @@  static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
 
 	mutex_lock(&wl->mutex);
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl12xx_for_each_wlvif(wl, wlvif) {
 		struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
@@ -4696,7 +4744,7 @@  static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
 		}
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -4725,9 +4773,11 @@  static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
 	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wlvif->band = ctx->def.chan->band;
 	wlvif->channel = channel;
@@ -4743,7 +4793,7 @@  static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
 		wlvif->radar_enabled = true;
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -4774,9 +4824,11 @@  static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
 	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	if (wlvif->radar_enabled) {
 		wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
@@ -4784,7 +4836,7 @@  static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
 		wlvif->radar_enabled = false;
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -4841,9 +4893,11 @@  wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
 
 	mutex_lock(&wl->mutex);
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	for (i = 0; i < n_vifs; i++) {
 		struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif);
@@ -4853,7 +4907,7 @@  wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
 			goto out_sleep;
 	}
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -4884,9 +4938,11 @@  static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
 	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/*
 	 * the txop is confed in units of 32us by the mac80211,
@@ -4905,7 +4961,7 @@  static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
 				 0, 0);
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -4929,16 +4985,18 @@  static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime);
 	if (ret < 0)
 		goto out_sleep;
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -5244,13 +5302,15 @@  static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	if (new_state < old_state)
@@ -5299,9 +5359,11 @@  static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 
 	ba_bitmap = &wl->links[hlid].ba_bitmap;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
 		     tid, action);
@@ -5374,7 +5436,7 @@  static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 		ret = -EINVAL;
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -5408,16 +5470,18 @@  static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
 	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
 	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
 
-		ret = wl1271_ps_elp_wakeup(wl);
-		if (ret < 0)
+		ret = pm_runtime_get_sync(wl->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(wl->dev);
 			goto out;
+		}
 
 		wl1271_set_band_rate(wl, wlvif);
 		wlvif->basic_rate =
 			wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
 		ret = wl1271_acx_sta_rate_policies(wl, wlvif);
 
-		wl1271_ps_elp_sleep(wl);
+		pm_runtime_put(wl->dev);
 	}
 out:
 	mutex_unlock(&wl->mutex);
@@ -5447,9 +5511,11 @@  static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	/* TODO: change mac80211 to pass vif as param */
 
@@ -5471,7 +5537,7 @@  static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
 	}
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -5538,9 +5604,11 @@  static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl->ops->channel_switch(wl, wlvif, &ch_switch);
 	if (ret)
@@ -5549,7 +5617,7 @@  static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw,
 	set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
@@ -5590,9 +5658,11 @@  static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
 	if (ret < 0)
@@ -5602,7 +5672,7 @@  static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
 	ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
 				     msecs_to_jiffies(duration));
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 	return ret;
@@ -5644,13 +5714,15 @@  static int wlcore_roc_completed(struct wl1271 *wl)
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = __wlcore_roc_completed(wl);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -5725,9 +5797,11 @@  static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out_sleep;
+	}
 
 	ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);
 	if (ret < 0)
@@ -5737,7 +5811,7 @@  static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
 	sinfo->signal = rssi_dbm;
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 out:
 	mutex_unlock(&wl->mutex);
@@ -6306,7 +6380,6 @@  struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
 	skb_queue_head_init(&wl->deferred_rx_queue);
 	skb_queue_head_init(&wl->deferred_tx_queue);
 
-	INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
 	INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
 	INIT_WORK(&wl->tx_work, wl1271_tx_work);
 	INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
@@ -6581,6 +6654,99 @@  static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 	complete_all(&wl->nvs_loading_complete);
 }
 
+static int __maybe_unused wlcore_runtime_suspend(struct device *dev)
+{
+	struct wl1271 *wl = dev_get_drvdata(dev);
+	struct wl12xx_vif *wlvif;
+	int error;
+
+	/* We do not enter elp sleep in PLT mode */
+	if (wl->plt)
+		return -EINVAL;
+
+	/* Nothing to do if no ELP mode requested */
+	if (wl->sleep_auth != WL1271_PSM_ELP)
+		return 0;
+
+	wl12xx_for_each_wlvif(wl, wlvif) {
+		if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
+		    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
+			return -EBUSY;
+	}
+
+	wl1271_debug(DEBUG_PSM, "chip to elp");
+	error = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_SLEEP);
+	if (error < 0) {
+		wl12xx_queue_recovery_work(wl);
+
+		return error;
+	}
+
+	set_bit(WL1271_FLAG_IN_ELP, &wl->flags);
+
+	return 0;
+}
+
+static int __maybe_unused wlcore_runtime_resume(struct device *dev)
+{
+	struct wl1271 *wl = dev_get_drvdata(dev);
+	DECLARE_COMPLETION_ONSTACK(compl);
+	unsigned long flags;
+	int ret;
+	unsigned long start_time = jiffies;
+	bool pending = false;
+
+	/* Nothing to do if no ELP mode requested */
+	if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
+		return 0;
+
+	wl1271_debug(DEBUG_PSM, "waking up chip from elp");
+
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
+		pending = true;
+	else
+		wl->elp_compl = &compl;
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+	ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
+	if (ret < 0) {
+		wl12xx_queue_recovery_work(wl);
+		goto err;
+	}
+
+	if (!pending) {
+		ret = wait_for_completion_timeout(&compl,
+			msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
+		if (ret == 0) {
+			wl1271_error("ELP wakeup timeout!");
+			wl12xx_queue_recovery_work(wl);
+
+			/* Return no error for runtime PM for recovery */
+			return 0;
+		}
+	}
+
+	clear_bit(WL1271_FLAG_IN_ELP, &wl->flags);
+
+	wl1271_debug(DEBUG_PSM, "wakeup time: %u ms",
+		     jiffies_to_msecs(jiffies - start_time));
+
+	return 0;
+
+err:
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	wl->elp_compl = NULL;
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+	return ret;
+}
+
+static const struct dev_pm_ops wlcore_pm_ops = {
+	SET_RUNTIME_PM_OPS(wlcore_runtime_suspend,
+			   wlcore_runtime_resume,
+			   NULL)
+};
+
 int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
 {
 	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
@@ -6608,6 +6774,9 @@  int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
 		wlcore_nvs_cb(NULL, wl);
 	}
 
+	wl->dev->driver->pm = &wlcore_pm_ops;
+	pm_runtime_enable(wl->dev);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wlcore_probe);
@@ -6616,6 +6785,13 @@  int wlcore_remove(struct platform_device *pdev)
 {
 	struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
 	struct wl1271 *wl = platform_get_drvdata(pdev);
+	int error;
+
+	error = pm_runtime_get_sync(wl->dev);
+	if (error < 0)
+		dev_warn(wl->dev, "PM runtime failed: %i\n", error);
+
+	wl->dev->driver->pm = NULL;
 
 	if (pdev_data->family && pdev_data->family->nvs_name)
 		wait_for_completion(&wl->nvs_loading_complete);
@@ -6627,6 +6803,10 @@  int wlcore_remove(struct platform_device *pdev)
 		disable_irq_wake(wl->irq);
 	}
 	wl1271_unregister_hw(wl);
+
+	pm_runtime_put_sync(wl->dev);
+	pm_runtime_disable(wl->dev);
+
 	free_irq(wl->irq, wl);
 	wlcore_free_hw(wl);
 
diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c
--- a/drivers/net/wireless/ti/wlcore/ps.c
+++ b/drivers/net/wireless/ti/wlcore/ps.c
@@ -26,152 +26,6 @@ 
 #include "tx.h"
 #include "debug.h"
 
-#define WL1271_WAKEUP_TIMEOUT 500
-
-#define ELP_ENTRY_DELAY  30
-#define ELP_ENTRY_DELAY_FORCE_PS  5
-
-void wl1271_elp_work(struct work_struct *work)
-{
-	struct delayed_work *dwork;
-	struct wl1271 *wl;
-	struct wl12xx_vif *wlvif;
-	int ret;
-
-	dwork = to_delayed_work(work);
-	wl = container_of(dwork, struct wl1271, elp_work);
-
-	wl1271_debug(DEBUG_PSM, "elp work");
-
-	mutex_lock(&wl->mutex);
-
-	if (unlikely(wl->state != WLCORE_STATE_ON))
-		goto out;
-
-	/* our work might have been already cancelled */
-	if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
-		goto out;
-
-	if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
-		goto out;
-
-	wl12xx_for_each_wlvif(wl, wlvif) {
-		if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
-		    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
-			goto out;
-	}
-
-	wl1271_debug(DEBUG_PSM, "chip to elp");
-	ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_SLEEP);
-	if (ret < 0) {
-		wl12xx_queue_recovery_work(wl);
-		goto out;
-	}
-
-	set_bit(WL1271_FLAG_IN_ELP, &wl->flags);
-
-out:
-	mutex_unlock(&wl->mutex);
-}
-
-/* Routines to toggle sleep mode while in ELP */
-void wl1271_ps_elp_sleep(struct wl1271 *wl)
-{
-	struct wl12xx_vif *wlvif;
-	u32 timeout;
-
-	/* We do not enter elp sleep in PLT mode */
-	if (wl->plt)
-		return;
-
-	if (wl->sleep_auth != WL1271_PSM_ELP)
-		return;
-
-	/* we shouldn't get consecutive sleep requests */
-	if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
-		return;
-
-	wl12xx_for_each_wlvif(wl, wlvif) {
-		if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
-		    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
-			return;
-	}
-
-	timeout = wl->conf.conn.forced_ps ?
-			ELP_ENTRY_DELAY_FORCE_PS : ELP_ENTRY_DELAY;
-	ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
-				     msecs_to_jiffies(timeout));
-}
-EXPORT_SYMBOL_GPL(wl1271_ps_elp_sleep);
-
-int wl1271_ps_elp_wakeup(struct wl1271 *wl)
-{
-	DECLARE_COMPLETION_ONSTACK(compl);
-	unsigned long flags;
-	int ret;
-	unsigned long start_time = jiffies;
-	bool pending = false;
-
-	/*
-	 * we might try to wake up even if we didn't go to sleep
-	 * before (e.g. on boot)
-	 */
-	if (!test_and_clear_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))
-		return 0;
-
-	/* don't cancel_sync as it might contend for a mutex and deadlock */
-	cancel_delayed_work(&wl->elp_work);
-
-	if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
-		return 0;
-
-	wl1271_debug(DEBUG_PSM, "waking up chip from elp");
-
-	/*
-	 * The spinlock is required here to synchronize both the work and
-	 * the completion variable in one entity.
-	 */
-	spin_lock_irqsave(&wl->wl_lock, flags);
-	if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
-		pending = true;
-	else
-		wl->elp_compl = &compl;
-	spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-	ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
-	if (ret < 0) {
-		wl12xx_queue_recovery_work(wl);
-		goto err;
-	}
-
-	if (!pending) {
-		ret = wait_for_completion_timeout(
-			&compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
-		if (ret == 0) {
-			wl1271_error("ELP wakeup timeout!");
-			wl12xx_queue_recovery_work(wl);
-			ret = -ETIMEDOUT;
-			goto err;
-		}
-	}
-
-	clear_bit(WL1271_FLAG_IN_ELP, &wl->flags);
-
-	wl1271_debug(DEBUG_PSM, "wakeup time: %u ms",
-		     jiffies_to_msecs(jiffies - start_time));
-	goto out;
-
-err:
-	spin_lock_irqsave(&wl->wl_lock, flags);
-	wl->elp_compl = NULL;
-	spin_unlock_irqrestore(&wl->wl_lock, flags);
-	return ret;
-
-out:
-	return 0;
-}
-EXPORT_SYMBOL_GPL(wl1271_ps_elp_wakeup);
-
 int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		       enum wl1271_cmd_ps_mode mode)
 {
diff --git a/drivers/net/wireless/ti/wlcore/ps.h b/drivers/net/wireless/ti/wlcore/ps.h
--- a/drivers/net/wireless/ti/wlcore/ps.h
+++ b/drivers/net/wireless/ti/wlcore/ps.h
@@ -29,9 +29,6 @@ 
 
 int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		       enum wl1271_cmd_ps_mode mode);
-void wl1271_ps_elp_sleep(struct wl1271 *wl);
-int wl1271_ps_elp_wakeup(struct wl1271 *wl);
-void wl1271_elp_work(struct work_struct *work);
 void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 			  u8 hlid, bool clean_queues);
 void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c
--- a/drivers/net/wireless/ti/wlcore/scan.c
+++ b/drivers/net/wireless/ti/wlcore/scan.c
@@ -22,13 +22,13 @@ 
  */
 
 #include <linux/ieee80211.h>
+#include <linux/pm_runtime.h>
 
 #include "wlcore.h"
 #include "debug.h"
 #include "cmd.h"
 #include "scan.h"
 #include "acx.h"
-#include "ps.h"
 #include "tx.h"
 
 void wl1271_scan_complete_work(struct work_struct *work)
@@ -67,16 +67,18 @@  void wl1271_scan_complete_work(struct work_struct *work)
 	wl->scan.req = NULL;
 	wl->scan_wlvif = NULL;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
 		/* restore hardware connection monitoring template */
 		wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq);
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
 	if (wl->scan.failed) {
 		wl1271_info("Scan completed due to error.");
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
--- a/drivers/net/wireless/ti/wlcore/sysfs.c
+++ b/drivers/net/wireless/ti/wlcore/sysfs.c
@@ -19,9 +19,11 @@ 
  *
  */
 
+#include <linux/pm_runtime.h>
+
+#include "acx.h"
 #include "wlcore.h"
 #include "debug.h"
-#include "ps.h"
 #include "sysfs.h"
 
 static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
@@ -68,12 +70,14 @@  static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
 	if (unlikely(wl->state != WLCORE_STATE_ON))
 		goto out;
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	wl1271_acx_sg_enable(wl, wl->sg_enabled);
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 
  out:
 	mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c
--- a/drivers/net/wireless/ti/wlcore/testmode.c
+++ b/drivers/net/wireless/ti/wlcore/testmode.c
@@ -22,13 +22,13 @@ 
  */
 #include "testmode.h"
 
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <net/genetlink.h>
 
 #include "wlcore.h"
 #include "debug.h"
 #include "acx.h"
-#include "ps.h"
 #include "io.h"
 
 #define WL1271_TM_MAX_DATA_LENGTH 1024
@@ -97,9 +97,11 @@  static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wl1271_cmd_test(wl, buf, buf_len, answer);
 	if (ret < 0) {
@@ -141,7 +143,7 @@  static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
 	}
 
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -169,9 +171,11 @@  static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 	if (!cmd) {
@@ -205,7 +209,7 @@  static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
 out_free:
 	kfree(cmd);
 out_sleep:
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -24,6 +24,7 @@ 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
+#include <linux/pm_runtime.h>
 #include <linux/spinlock.h>
 
 #include "wlcore.h"
@@ -868,9 +869,11 @@  void wl1271_tx_work(struct work_struct *work)
 	int ret;
 
 	mutex_lock(&wl->mutex);
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wlcore_tx_work_locked(wl);
 	if (ret < 0) {
@@ -878,7 +881,7 @@  void wl1271_tx_work(struct work_struct *work)
 		goto out;
 	}
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 }
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
--- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c
+++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
@@ -8,12 +8,13 @@ 
  * version 2 as published by the Free Software Foundation.
  */
 
+#include <linux/pm_runtime.h>
+
 #include <net/mac80211.h>
 #include <net/netlink.h>
 
 #include "wlcore.h"
 #include "debug.h"
-#include "ps.h"
 #include "hw_ops.h"
 #include "vendor_cmd.h"
 
@@ -55,14 +56,16 @@  wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wlcore_smart_config_start(wl,
 			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -87,13 +90,15 @@  wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wlcore_smart_config_stop(wl);
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
@@ -131,16 +136,18 @@  wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
 		goto out;
 	}
 
-	ret = wl1271_ps_elp_wakeup(wl);
-	if (ret < 0)
+	ret = pm_runtime_get_sync(wl->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(wl->dev);
 		goto out;
+	}
 
 	ret = wlcore_smart_config_set_group_key(wl,
 			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
 			nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
 			nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
 
-	wl1271_ps_elp_sleep(wl);
+	pm_runtime_put(wl->dev);
 out:
 	mutex_unlock(&wl->mutex);
 
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -348,7 +348,6 @@  struct wl1271 {
 	enum nl80211_band band;
 
 	struct completion *elp_compl;
-	struct delayed_work elp_work;
 
 	/* in dBm */
 	int power_level;
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -233,7 +233,6 @@  enum wl12xx_flags {
 	WL1271_FLAG_TX_QUEUE_STOPPED,
 	WL1271_FLAG_TX_PENDING,
 	WL1271_FLAG_IN_ELP,
-	WL1271_FLAG_ELP_REQUESTED,
 	WL1271_FLAG_IRQ_RUNNING,
 	WL1271_FLAG_FW_TX_BUSY,
 	WL1271_FLAG_DUMMY_PACKET_PENDING,