CC3351: When Wake on WLAN is enabled, but the system is woken up by another device such as RTC, the network card fails to resume properly

Part Number: CC3351

Tool/software:

I want to keep the WiFi connection alive, so I enabled WoWLAN. However, when I wake up the system using other sources, such as the RTC or the serial port, the WiFi interface does not resume properly.

Upon checking the kernel logs, I observed the following message:

[ 84.413671] wlcore: flushing tx buffer: 3 0  
[ 84.453673] wlcore: flushing tx buffer: 3 0  
[ 84.493674] wlcore: WARNING Unable to flush all TX buffers, timed out (timeout 500 ms  
[ 84.507403] wlcore: flushing remaining works  
[ 84.512096] CVTE::imx_thermal_suspend - temp=41277  
[ 84.521364] PM: suspend devices took 1.130 seconds  
[ 84.528715] PM: pm_system_irq_wakeup: 43 triggered rtc alarm  
[ 84.541226] CVTE::imx_thermal_resume - temp=38197  
[ 84.546341] wlcore: mac80211 resume wow=1  
[ 84.552052] PM: dpm_run_callback(): wiphy_resume+0x0/0x218 [cfg80211] returns -1662942080  
[ 84.560358] PM: Device phy0 failed to resume async: error -1662942080  
[ 84.567606] PM: resume devices took 0.030 seconds  
[ 84.572329] OOM killer enabled.  
[ 84.575637] Restarting tasks ... done.  
[ 84.606268] Resume caused by IRQ 43, rtc alarm  
[ 84.614251] PM: suspend exit

The resume callback function returns an unexpectedly large value

[ 84.552052] PM: dpm_run_callback(): wiphy_resume+0x0/0x218 [cfg80211] returns -1662942080  
[ 84.560358] PM: Device phy0 failed to resume async: error -1662942080  

So I checked the source code and found that the variable ret was not initialized, so the function returned an uninitialized value.

Here is the code after my edit:

static int __maybe_unused cc33xx_op_resume(struct ieee80211_hw *hw)
{
	struct cc33xx *wl = hw->priv;
	struct cc33xx_vif *wlvif;
	unsigned long flags;
	bool run_irq_work = false, pending_recovery;
	int ret = 0;// -----------------------------------here

	cc33xx_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
		     wl->keep_device_power);
	WARN_ON(!wl->keep_device_power);

	/*
	 * re-enable irq_work enqueuing, and call irq_work directly if
	 * there is a pending work.
	 */
	spin_lock_irqsave(&wl->wl_lock, flags);
	clear_bit(CC33XX_FLAG_SUSPENDED, &wl->flags);
	run_irq_work = test_and_clear_bit(CC33XX_FLAG_PENDING_WORK, &wl->flags);
	spin_unlock_irqrestore(&wl->wl_lock, flags);

	mutex_lock(&wl->mutex);

	/* test the recovery flag before calling any SDIO functions */
	pending_recovery = test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS,
				    &wl->flags);

	if (run_irq_work) {
		cc33xx_debug(DEBUG_MAC80211, "run postponed irq_work directly");

		/* don't talk to the HW if recovery is pending */
		if (!pending_recovery) {
			ret = wlcore_irq_locked(wl);
			if (ret)
				cc33xx_queue_recovery_work(wl);
		}

		wlcore_enable_interrupts(wl);
	}

	if (pending_recovery) {
		cc33xx_warning("queuing forgotten recovery on resume");
		ieee80211_queue_work(wl->hw, &wl->recovery_work);
		goto out;
	}

	cc33xx_for_each_wlvif(wl, wlvif) {
		if (wlcore_is_p2p_mgmt(wlvif))
			continue;

		cc33xx_configure_resume(wl, wlvif);
	}

out:
	wl->keep_device_power = false;

	/*
	 * Set a flag to re-init the watchdog on the first Tx after resume.
	 * That way we avoid possible conditions where Tx-complete interrupts
	 * fail to arrive and we perform a spurious recovery.
	 */
	set_bit(CC33XX_FLAG_REINIT_TX_WDOG, &wl->flags);
	mutex_unlock(&wl->mutex);

	return ret;
}