diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -3046,7 +3047,8 @@ static void btintel_pcie_coredump(struct device *dev) static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) { - int retry = 0, status; + int retry = 0; + long status; u32 dx_intr_timeout_ms = 200; do { @@ -3098,18 +3100,23 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) data = pci_get_drvdata(pdev); - dxstate = (mesg.event == PM_EVENT_SUSPEND ? - BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD); - - data->pm_sx_event = mesg.event; + /* S0ix (s2idle) uses D3_HOT; S3, freeze and hibernate use D3_COLD. */ + if (mesg.event == PM_EVENT_SUSPEND && + pm_suspend_target_state == PM_SUSPEND_TO_IDLE) + dxstate = BTINTEL_PCIE_STATE_D3_HOT; + else + dxstate = BTINTEL_PCIE_STATE_D3_COLD; start = ktime_get(); /* Refer: 6.4.11.7 -> Platform power management */ err = btintel_pcie_set_dxstate(data, dxstate); - if (err) + if (err) { + bt_dev_err(data->hdev, "Failed to set dxstate:%u (%d)", + dxstate, err); return err; + } bt_dev_dbg(data->hdev, "device entered into d3 state from d0 in %lld us", @@ -3132,7 +3139,7 @@ static int btintel_pcie_freeze(struct device *dev) return btintel_pcie_suspend_late(dev, PMSG_FREEZE); } -static int btintel_pcie_resume(struct device *dev) +static int btintel_pcie_resume_event(struct device *dev, pm_message_t mesg) { struct pci_dev *pdev = to_pci_dev(dev); struct btintel_pcie_data *data; @@ -3140,19 +3147,15 @@ static int btintel_pcie_resume(struct device *dev) int err; data = pci_get_drvdata(pdev); - data->gp0_received = false; start = ktime_get(); - /* When the system enters S4 (hibernate) mode, bluetooth device loses - * power, which results in the erasure of its loaded firmware. - * Consequently, function level reset (flr) is required on system - * resume to bring the controller back into an operational state by - * initiating a new firmware download. + /* S3 and S4 may cut power, erasing the firmware. Force FLR to recover + * instead of a normal D0 transition. */ - - if (data->pm_sx_event == PM_EVENT_FREEZE || - data->pm_sx_event == PM_EVENT_HIBERNATE) { + if (mesg.event == PM_EVENT_RESTORE || + (mesg.event == PM_EVENT_RESUME && + pm_suspend_target_state == PM_SUSPEND_MEM)) { set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); return 0; @@ -3161,7 +3164,9 @@ static int btintel_pcie_resume(struct device *dev) /* Refer: 6.4.11.7 -> Platform power management */ err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0); - if (err == 0) { + if (err) { + bt_dev_err(data->hdev, "Failed to set D0 state (%d)", err); + } else { bt_dev_dbg(data->hdev, "device entered into d0 state from d3 in %lld us", ktime_to_us(ktime_get() - start)); @@ -3190,13 +3195,28 @@ static int btintel_pcie_resume(struct device *dev) return err; } +static int btintel_pcie_resume(struct device *dev) +{ + return btintel_pcie_resume_event(dev, PMSG_RESUME); +} + +static int btintel_pcie_restore(struct device *dev) +{ + return btintel_pcie_resume_event(dev, PMSG_RESTORE); +} + +static int btintel_pcie_thaw(struct device *dev) +{ + return btintel_pcie_resume_event(dev, PMSG_THAW); +} + static const struct dev_pm_ops btintel_pcie_pm_ops = { .suspend = btintel_pcie_suspend, .resume = btintel_pcie_resume, .freeze = btintel_pcie_freeze, - .thaw = btintel_pcie_resume, + .thaw = btintel_pcie_thaw, .poweroff = btintel_pcie_hibernate, - .restore = btintel_pcie_resume, + .restore = btintel_pcie_restore, }; static struct pci_driver btintel_pcie_driver = { diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -478,7 +478,6 @@ struct btintel_pcie_dump_header { * @txq: TX Queue struct * @rxq: RX Queue struct * @alive_intr_ctxt: Alive interrupt context - * @pm_sx_event: PM event on which system got suspended */ struct btintel_pcie_data { struct pci_dev *pdev; @@ -533,7 +532,6 @@ struct btintel_pcie_data { enum btintel_pcie_reset_type reset_type; struct btintel_pcie_dbgc dbgc; struct btintel_pcie_dump_header dmp_hdr; - u8 pm_sx_event; u32 debug_evt_addr; u32 debug_evt_size; };