Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Bluetooth: btintel_pcie: Support for S4 (Hibernate)

During S4 (hibernate), the Bluetooth device loses power. Upon resume,
the driver performs the following actions:

1. Unregisters hdev
2. Calls function level reset
3. Registers hdev

Test case:
- run command sudo rtcwake -m disk -s 60

Signed-off-by: Ravindra <ravindra@intel.com>
Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

authored by

Ravindra and committed by
Luiz Augusto von Dentz
1fb0d830 5a6700a3

+43
+41
drivers/bluetooth/btintel_pcie.c
··· 825 825 return !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY); 826 826 } 827 827 828 + static inline bool btintel_pcie_in_device_halt(struct btintel_pcie_data *data) 829 + { 830 + return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED; 831 + } 832 + 828 833 static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data, 829 834 u32 dxstate) 830 835 { ··· 2537 2532 dxstate = (mesg.event == PM_EVENT_SUSPEND ? 2538 2533 BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD); 2539 2534 2535 + data->pm_sx_event = mesg.event; 2536 + 2540 2537 data->gp0_received = false; 2541 2538 2542 2539 start = ktime_get(); ··· 2588 2581 2589 2582 start = ktime_get(); 2590 2583 2584 + /* When the system enters S4 (hibernate) mode, bluetooth device loses 2585 + * power, which results in the erasure of its loaded firmware. 2586 + * Consequently, function level reset (flr) is required on system 2587 + * resume to bring the controller back into an operational state by 2588 + * initiating a new firmware download. 2589 + */ 2590 + 2591 + if (data->pm_sx_event == PM_EVENT_FREEZE || 2592 + data->pm_sx_event == PM_EVENT_HIBERNATE) { 2593 + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); 2594 + btintel_pcie_reset(data->hdev); 2595 + return 0; 2596 + } 2597 + 2591 2598 /* Refer: 6.4.11.7 -> Platform power management */ 2592 2599 btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0); 2593 2600 err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, ··· 2610 2589 bt_dev_err(data->hdev, 2611 2590 "Timeout (%u ms) on alive interrupt for D0 entry", 2612 2591 BTINTEL_DEFAULT_INTR_TIMEOUT_MS); 2592 + 2593 + /* Trigger function level reset if the controller is in error 2594 + * state during resume() to bring back the controller to 2595 + * operational mode 2596 + */ 2597 + 2598 + data->boot_stage_cache = btintel_pcie_rd_reg32(data, 2599 + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); 2600 + if (btintel_pcie_in_error(data) || 2601 + btintel_pcie_in_device_halt(data)) { 2602 + bt_dev_err(data->hdev, "Controller in error state for D0 entry"); 2603 + if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, 2604 + &data->flags)) { 2605 + data->dmp_hdr.trigger_reason = 2606 + BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; 2607 + queue_work(data->workqueue, &data->rx_work); 2608 + } 2609 + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); 2610 + btintel_pcie_reset(data->hdev); 2611 + } 2613 2612 return -EBUSY; 2614 2613 } 2615 2614
+2
drivers/bluetooth/btintel_pcie.h
··· 464 464 * @txq: TX Queue struct 465 465 * @rxq: RX Queue struct 466 466 * @alive_intr_ctxt: Alive interrupt context 467 + * @pm_sx_event: PM event on which system got suspended 467 468 */ 468 469 struct btintel_pcie_data { 469 470 struct pci_dev *pdev; ··· 514 513 u32 alive_intr_ctxt; 515 514 struct btintel_pcie_dbgc dbgc; 516 515 struct btintel_pcie_dump_header dmp_hdr; 516 + u8 pm_sx_event; 517 517 }; 518 518 519 519 static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,