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

Configure Feed

Select the types of activity you want to include in your feed.

Bluetooth: btintel_pcie: Add support for _suspend() / _resume()

This patch implements _suspend() and _resume() functions for the
Bluetooth controller. When the system enters a suspended state, the
driver notifies the controller to perform necessary housekeeping tasks
by writing to the sleep control register and waits for an alive
interrupt. The firmware raises the alive interrupt when it has
transitioned to the D3 state. The same flow occurs when the system
resumes.

Command to test host initiated wakeup after 60 seconds
sudo rtcwake -m mem -s 60

dmesg log (tested on Whale Peak2 on Panther Lake platform)
On system suspend:
[Fri Jul 25 11:05:37 2025] Bluetooth: hci0: device entered into d3 state from d0 in 80 us

On system resume:
[Fri Jul 25 11:06:36 2025] Bluetooth: hci0: device entered into d0 state from d3 in 7117 us

Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

authored by

Chandrashekar Devegowda and committed by
Luiz Augusto von Dentz
e57362f4 926e8bfa

+89
+89
drivers/bluetooth/btintel_pcie.c
··· 2574 2574 } 2575 2575 #endif 2576 2576 2577 + static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) 2578 + { 2579 + struct pci_dev *pdev = to_pci_dev(dev); 2580 + struct btintel_pcie_data *data; 2581 + ktime_t start; 2582 + u32 dxstate; 2583 + int err; 2584 + 2585 + data = pci_get_drvdata(pdev); 2586 + 2587 + dxstate = (mesg.event == PM_EVENT_SUSPEND ? 2588 + BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD); 2589 + 2590 + data->gp0_received = false; 2591 + 2592 + start = ktime_get(); 2593 + 2594 + /* Refer: 6.4.11.7 -> Platform power management */ 2595 + btintel_pcie_wr_sleep_cntrl(data, dxstate); 2596 + err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, 2597 + msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); 2598 + if (err == 0) { 2599 + bt_dev_err(data->hdev, 2600 + "Timeout (%u ms) on alive interrupt for D3 entry", 2601 + BTINTEL_DEFAULT_INTR_TIMEOUT_MS); 2602 + return -EBUSY; 2603 + } 2604 + 2605 + bt_dev_dbg(data->hdev, 2606 + "device entered into d3 state from d0 in %lld us", 2607 + ktime_to_us(ktime_get() - start)); 2608 + 2609 + return 0; 2610 + } 2611 + 2612 + static int btintel_pcie_suspend(struct device *dev) 2613 + { 2614 + return btintel_pcie_suspend_late(dev, PMSG_SUSPEND); 2615 + } 2616 + 2617 + static int btintel_pcie_hibernate(struct device *dev) 2618 + { 2619 + return btintel_pcie_suspend_late(dev, PMSG_HIBERNATE); 2620 + } 2621 + 2622 + static int btintel_pcie_freeze(struct device *dev) 2623 + { 2624 + return btintel_pcie_suspend_late(dev, PMSG_FREEZE); 2625 + } 2626 + 2627 + static int btintel_pcie_resume(struct device *dev) 2628 + { 2629 + struct pci_dev *pdev = to_pci_dev(dev); 2630 + struct btintel_pcie_data *data; 2631 + ktime_t start; 2632 + int err; 2633 + 2634 + data = pci_get_drvdata(pdev); 2635 + data->gp0_received = false; 2636 + 2637 + start = ktime_get(); 2638 + 2639 + /* Refer: 6.4.11.7 -> Platform power management */ 2640 + btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0); 2641 + err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, 2642 + msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); 2643 + if (err == 0) { 2644 + bt_dev_err(data->hdev, 2645 + "Timeout (%u ms) on alive interrupt for D0 entry", 2646 + BTINTEL_DEFAULT_INTR_TIMEOUT_MS); 2647 + return -EBUSY; 2648 + } 2649 + 2650 + bt_dev_dbg(data->hdev, 2651 + "device entered into d0 state from d3 in %lld us", 2652 + ktime_to_us(ktime_get() - start)); 2653 + return 0; 2654 + } 2655 + 2656 + static const struct dev_pm_ops btintel_pcie_pm_ops = { 2657 + .suspend = btintel_pcie_suspend, 2658 + .resume = btintel_pcie_resume, 2659 + .freeze = btintel_pcie_freeze, 2660 + .thaw = btintel_pcie_resume, 2661 + .poweroff = btintel_pcie_hibernate, 2662 + .restore = btintel_pcie_resume, 2663 + }; 2664 + 2577 2665 static struct pci_driver btintel_pcie_driver = { 2578 2666 .name = KBUILD_MODNAME, 2579 2667 .id_table = btintel_pcie_table, 2580 2668 .probe = btintel_pcie_probe, 2581 2669 .remove = btintel_pcie_remove, 2670 + .driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops), 2582 2671 #ifdef CONFIG_DEV_COREDUMP 2583 2672 .driver.coredump = btintel_pcie_coredump 2584 2673 #endif