Skip to content

Commit cb347bd

Browse files
egrumbachjmberg-intel
authored andcommitted
wifi: iwlwifi: mvm: fix hibernation
Fast resume is a feature that was recently introduced to speed up the resume time. It basically keeps the firmware alive while the system is suspended and that avoids starting again the whole device. This flow can't work for hibernation, since when the system boots, before the frozen image is loaded, the kernel may touch the device. As a result, we can't assume the device is in the exact same state as before the hibernation. Detect that we are resuming from hibernation through the PCI device and forbid the fast resume flow. We also need to shut down the device cleanly when that happens. In addition, in case the device is power gated during S3, we won't be able to keep the device alive. Detect this situation with BE200 at least with the help of the CSR_FUNC_SCRATCH register and reset the device upon resume if it was power gated during S3. Fixes: e8bb19c ("wifi: iwlwifi: support fast resume") Signed-off-by: Emmanuel Grumbach <[email protected]> Signed-off-by: Miri Korenblit <[email protected]> Link: https://patch.msgid.link/20240825191257.24eb3b19e74f.I3837810318dbef0a0a773cf4c4fcf89cdc6fdbd3@changeid Signed-off-by: Johannes Berg <[email protected]>
1 parent 3e9b402 commit cb347bd

File tree

4 files changed

+76
-4
lines changed

4 files changed

+76
-4
lines changed

drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ struct iwl_cfg;
8585
* May sleep
8686
* @wimax_active: invoked when WiMax becomes active. May sleep
8787
* @time_point: called when transport layer wants to collect debug data
88+
* @device_powered_off: called upon resume from hibernation but not only.
89+
* Op_mode needs to reset its internal state because the device did not
90+
* survive the system state transition. The firmware is no longer running,
91+
* etc...
8892
*/
8993
struct iwl_op_mode_ops {
9094
struct iwl_op_mode *(*start)(struct iwl_trans *trans,
@@ -107,6 +111,7 @@ struct iwl_op_mode_ops {
107111
void (*time_point)(struct iwl_op_mode *op_mode,
108112
enum iwl_fw_ini_time_point tp_id,
109113
union iwl_dbg_tlv_tp_data *tp_data);
114+
void (*device_powered_off)(struct iwl_op_mode *op_mode);
110115
};
111116

112117
int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
@@ -204,4 +209,11 @@ static inline void iwl_op_mode_time_point(struct iwl_op_mode *op_mode,
204209
op_mode->ops->time_point(op_mode, tp_id, tp_data);
205210
}
206211

212+
static inline void iwl_op_mode_device_powered_off(struct iwl_op_mode *op_mode)
213+
{
214+
if (!op_mode || !op_mode->ops || !op_mode->ops->device_powered_off)
215+
return;
216+
op_mode->ops->device_powered_off(op_mode);
217+
}
218+
207219
#endif /* __iwl_op_mode_h__ */

drivers/net/wireless/intel/iwlwifi/mvm/d3.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3439,6 +3439,16 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
34393439

34403440
mutex_lock(&mvm->mutex);
34413441

3442+
/* Apparently, the device went away and device_powered_off() was called,
3443+
* don't even try to read the rt_status, the device is currently
3444+
* inaccessible.
3445+
*/
3446+
if (!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status)) {
3447+
IWL_INFO(mvm,
3448+
"Can't resume, device_powered_off() was called during wowlan\n");
3449+
goto err;
3450+
}
3451+
34423452
mvm->last_reset_or_resume_time_jiffies = jiffies;
34433453

34443454
/* get the BSS vif pointer again */

drivers/net/wireless/intel/iwlwifi/mvm/ops.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,20 @@ static void iwl_op_mode_mvm_time_point(struct iwl_op_mode *op_mode,
20902090
iwl_dbg_tlv_time_point(&mvm->fwrt, tp_id, tp_data);
20912091
}
20922092

2093+
static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode)
2094+
{
2095+
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
2096+
2097+
mutex_lock(&mvm->mutex);
2098+
clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
2099+
mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
2100+
iwl_mvm_stop_device(mvm);
2101+
#ifdef CONFIG_PM
2102+
mvm->fast_resume = false;
2103+
#endif
2104+
mutex_unlock(&mvm->mutex);
2105+
}
2106+
20932107
#define IWL_MVM_COMMON_OPS \
20942108
/* these could be differentiated */ \
20952109
.queue_full = iwl_mvm_stop_sw_queue, \
@@ -2102,7 +2116,8 @@ static void iwl_op_mode_mvm_time_point(struct iwl_op_mode *op_mode,
21022116
/* as we only register one, these MUST be common! */ \
21032117
.start = iwl_op_mode_mvm_start, \
21042118
.stop = iwl_op_mode_mvm_stop, \
2105-
.time_point = iwl_op_mode_mvm_time_point
2119+
.time_point = iwl_op_mode_mvm_time_point, \
2120+
.device_powered_off = iwl_op_mode_mvm_device_powered_off
21062121

21072122
static const struct iwl_op_mode_ops iwl_mvm_ops = {
21082123
IWL_MVM_COMMON_OPS,

drivers/net/wireless/intel/iwlwifi/pcie/drv.c

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,11 +1577,12 @@ static int iwl_pci_suspend(struct device *device)
15771577
return 0;
15781578
}
15791579

1580-
static int iwl_pci_resume(struct device *device)
1580+
static int _iwl_pci_resume(struct device *device, bool restore)
15811581
{
15821582
struct pci_dev *pdev = to_pci_dev(device);
15831583
struct iwl_trans *trans = pci_get_drvdata(pdev);
15841584
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
1585+
bool device_was_powered_off = false;
15851586

15861587
/* Before you put code here, think about WoWLAN. You cannot check here
15871588
* whether WoWLAN is enabled or not, and your code will run even if
@@ -1597,6 +1598,26 @@ static int iwl_pci_resume(struct device *device)
15971598
if (!trans->op_mode)
15981599
return 0;
15991600

1601+
/*
1602+
* Scratch value was altered, this means the device was powered off, we
1603+
* need to reset it completely.
1604+
* Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan,
1605+
* so assume that any bits there mean that the device is usable.
1606+
*/
1607+
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ &&
1608+
!iwl_read32(trans, CSR_FUNC_SCRATCH))
1609+
device_was_powered_off = true;
1610+
1611+
if (restore || device_was_powered_off) {
1612+
trans->state = IWL_TRANS_NO_FW;
1613+
/* Hope for the best here ... If one of those steps fails we
1614+
* won't really know how to recover.
1615+
*/
1616+
iwl_pcie_prepare_card_hw(trans);
1617+
iwl_finish_nic_init(trans);
1618+
iwl_op_mode_device_powered_off(trans->op_mode);
1619+
}
1620+
16001621
/* In WOWLAN, let iwl_trans_pcie_d3_resume do the rest of the work */
16011622
if (test_bit(STATUS_DEVICE_ENABLED, &trans->status))
16021623
return 0;
@@ -1617,9 +1638,23 @@ static int iwl_pci_resume(struct device *device)
16171638
return 0;
16181639
}
16191640

1641+
static int iwl_pci_restore(struct device *device)
1642+
{
1643+
return _iwl_pci_resume(device, true);
1644+
}
1645+
1646+
static int iwl_pci_resume(struct device *device)
1647+
{
1648+
return _iwl_pci_resume(device, false);
1649+
}
1650+
16201651
static const struct dev_pm_ops iwl_dev_pm_ops = {
1621-
SET_SYSTEM_SLEEP_PM_OPS(iwl_pci_suspend,
1622-
iwl_pci_resume)
1652+
.suspend = pm_sleep_ptr(iwl_pci_suspend),
1653+
.resume = pm_sleep_ptr(iwl_pci_resume),
1654+
.freeze = pm_sleep_ptr(iwl_pci_suspend),
1655+
.thaw = pm_sleep_ptr(iwl_pci_resume),
1656+
.poweroff = pm_sleep_ptr(iwl_pci_suspend),
1657+
.restore = pm_sleep_ptr(iwl_pci_restore),
16231658
};
16241659

16251660
#define IWL_PM_OPS (&iwl_dev_pm_ops)

0 commit comments

Comments
 (0)