Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c57c77e446 | |||
| db4ea70fb5 |
+85
-9
@@ -524,7 +524,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
|||||||
bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
|
bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
|
||||||
__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
|
__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
|
||||||
priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
|
priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
|
||||||
|
/*
|
||||||
|
* Reinit BEFORE the WSM goes out, so a stale
|
||||||
|
* indication from a previous cycle cannot have
|
||||||
|
* primed pm_enter_cmpl. From here until the
|
||||||
|
* indication callback's cmpxchg(1->0) on
|
||||||
|
* pm_set_in_process, only the indication for
|
||||||
|
* THIS request can complete the wait.
|
||||||
|
*/
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
|
||||||
|
|
||||||
ret = bes2600_set_pm(priv, &priv->powersave_mode);
|
ret = bes2600_set_pm(priv, &priv->powersave_mode);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
@@ -535,11 +545,33 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
|||||||
|
|
||||||
/* wait power save mode changed indication */
|
/* wait power save mode changed indication */
|
||||||
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
|
||||||
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
bes_devel("%s, wait pm ind timeout\n", __func__);
|
/*
|
||||||
timeouts++;
|
* The indication callback only fires
|
||||||
|
* complete() when it observes
|
||||||
|
* pm_set_in_process == 1; cmpxchg it
|
||||||
|
* to 0 here so a late indication
|
||||||
|
* cannot prime the next wait.
|
||||||
|
*
|
||||||
|
* If we win the cmpxchg, this is a
|
||||||
|
* real timeout: the firmware's PS
|
||||||
|
* state is unknown to us. Mark it as
|
||||||
|
* such so the next wake path can
|
||||||
|
* probe before assuming the chip is
|
||||||
|
* still active.
|
||||||
|
*
|
||||||
|
* If we lose the cmpxchg, the
|
||||||
|
* indication arrived between the
|
||||||
|
* wait timing out and us getting
|
||||||
|
* here; treat as success.
|
||||||
|
*/
|
||||||
|
if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
1, 0) == 1) {
|
||||||
|
bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
timeouts++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bes_devel("skip enter lp mode\n");
|
bes_devel("skip enter lp mode\n");
|
||||||
@@ -554,10 +586,34 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
|||||||
* in an inconsistent state that cascades into SDIO TX errors on
|
* in an inconsistent state that cascades into SDIO TX errors on
|
||||||
* the BES2600.
|
* the BES2600.
|
||||||
*/
|
*/
|
||||||
if (timeouts == 0)
|
if (timeouts == 0) {
|
||||||
bes2600_pwr_device_enter_lp_mode(hw_priv);
|
bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
else
|
} else {
|
||||||
|
/*
|
||||||
|
* device_enter_lp_mode() was skipped (one or more VIFs
|
||||||
|
* timed out waiting for the firmware indication) so its
|
||||||
|
* gpio_sleep(MCU) - which drops the wake-flag bit and, if
|
||||||
|
* no other subsystem holds the wake, drives the GPIO low -
|
||||||
|
* never ran. Without it the bit stays asserted, and the
|
||||||
|
* next bes2600_pwr_device_exit_lp_mode() calls
|
||||||
|
* gpio_wake(MCU) into a "bit already set" no-op: the GPIO
|
||||||
|
* never re-edges, sbus_active() exhausts its 200x2ms
|
||||||
|
* MCU_WAKEUP_READY budget against an unwoken chip, and
|
||||||
|
* the first TX after idle stalls for several seconds.
|
||||||
|
*
|
||||||
|
* Drop the MCU wake-flag bit explicitly here so the next
|
||||||
|
* wake injects a real GPIO edge. gpio_allow_mcu_sleep
|
||||||
|
* preserves multi-subsystem semantics: it only drives the
|
||||||
|
* GPIO low when no other subsystem still holds wake; if
|
||||||
|
* BT or another holder is keeping the chip awake, the
|
||||||
|
* GPIO stays high and the bit clear here is purely
|
||||||
|
* bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
*/
|
||||||
|
if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
GPIO_WAKE_FLAG_MCU);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -833,6 +889,7 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
|||||||
hw_priv->bes_power.power_up_task = NULL;
|
hw_priv->bes_power.power_up_task = NULL;
|
||||||
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
device_set_wakeup_capable(hw_priv->pdev, true);
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
@@ -1213,9 +1270,28 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event)
|
|||||||
|
|
||||||
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
{
|
{
|
||||||
if((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
/*
|
||||||
bes_devel("complete pm_enter_cmpl\n");
|
* The firmware sends a PM-changed indication for every transition,
|
||||||
complete(&hw_priv->bes_power.pm_enter_cmpl);
|
* including ones we didn't ask for (firmware-internal coex moves,
|
||||||
|
* idle-driven aging). Update chip_pm_state unconditionally so the
|
||||||
|
* wake path can use it, but only fire pm_enter_cmpl when a host-
|
||||||
|
* initiated set_pm is actually in flight - otherwise a stale
|
||||||
|
* indication can prime a future wait against a freshly
|
||||||
|
* reinit_completion()'ed state.
|
||||||
|
*/
|
||||||
|
if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_LP);
|
||||||
|
if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
1, 0) == 1) {
|
||||||
|
bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
} else {
|
||||||
|
bes_devel("PM ind (LP) without pending wait; state recorded\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_ACTIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,20 @@ enum power_down_state
|
|||||||
POWER_DOWN_STATE_UNLOCKED,
|
POWER_DOWN_STATE_UNLOCKED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Confirmed PM state of the firmware-side chip. Tracks what the host
|
||||||
|
* has *seen* the firmware acknowledge, not what the host has
|
||||||
|
* requested. UNKNOWN means a host-initiated transition timed out
|
||||||
|
* before the firmware indication arrived; the next wake path should
|
||||||
|
* treat it as "we don't know" and probe before issuing GPIO/SDIO
|
||||||
|
* wakeup ops.
|
||||||
|
*/
|
||||||
|
enum bes2600_chip_pm_state {
|
||||||
|
BES2600_CHIP_PM_ACTIVE = 0,
|
||||||
|
BES2600_CHIP_PM_LP,
|
||||||
|
BES2600_CHIP_PM_UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
@@ -106,6 +120,7 @@ struct bes2600_pwr_t
|
|||||||
bool ap_lp_bad;
|
bool ap_lp_bad;
|
||||||
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
atomic_t pm_set_in_process;
|
atomic_t pm_set_in_process;
|
||||||
|
atomic_t chip_pm_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_BES2600_WOWLAN
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
|||||||
+15
-2
@@ -22,9 +22,17 @@
|
|||||||
* After this many consecutive WSM scan rejections from firmware, stop
|
* After this many consecutive WSM scan rejections from firmware, stop
|
||||||
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
* that's rejecting them (coex window, firmware-internal busy) clear.
|
* that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
*
|
||||||
|
* The backoff has to be at least as long as the natural mac80211 scan-
|
||||||
|
* retry cadence, otherwise the next attempt lands outside the window
|
||||||
|
* and bypasses the defer guard. Observed in the wild on PineTab2:
|
||||||
|
* roam-evaluation bursts at ~12 s cadence, idle background scans at
|
||||||
|
* ~5 min cadence. 30 s catches the burst and leaves the slow case
|
||||||
|
* alone (the firmware-policy state has had minutes to clear by then
|
||||||
|
* anyway).
|
||||||
*/
|
*/
|
||||||
#define BES2600_SCAN_REJECT_THRESHOLD 3
|
#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ)
|
||||||
|
|
||||||
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
@@ -40,7 +48,9 @@ static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
|||||||
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
* rejections on recent scan attempts and the backoff window has
|
* rejections on recent scan attempts and the backoff window has
|
||||||
* not yet elapsed. Whatever was rejecting them is likely still
|
* not yet elapsed. Whatever was rejecting them is likely still
|
||||||
* rejecting them; give it time.
|
* rejecting them; give it time. If the backoff has elapsed without
|
||||||
|
* a fresh reject refreshing it, the burst is over and we reset the
|
||||||
|
* count so an isolated reject doesn't immediately re-trip.
|
||||||
*
|
*
|
||||||
* Returns true if the caller should abandon the scan iteration.
|
* Returns true if the caller should abandon the scan iteration.
|
||||||
*/
|
*/
|
||||||
@@ -51,6 +61,9 @@ static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
|||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (time_after(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
hw_priv->scan.reject_count = 0;
|
||||||
|
|
||||||
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
time_before(jiffies, hw_priv->scan.backoff_until))
|
time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user