Re: [PATCH v6] Bluetooth: btintel_pcie: Add support for _suspend() / _resume()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Kiran,

On Fri, Jul 25, 2025 at 4:45 AM Kiran K <kiran.k@xxxxxxxxx> wrote:
>
> From: Chandrashekar Devegowda <chandrashekar.devegowda@xxxxxxxxx>
>
> 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@xxxxxxxxx>
> Signed-off-by: Kiran K <kiran.k@xxxxxxxxx>
> ---
> changes in v6:
>      - s/delta/delta_us/g
>      - s/CONFIG_PM/CONFIG_PM_SLEEP/g
>      - use pm_sleep_pr()/pm_str() to avoid #ifdefs
>      - remove the code to set persistance mode as its not relevant to this patch
>
> changes in v5:
>      - refactor _suspend() / _resume() to set the D3HOT/D3COLD based on power
>        event
>      - remove SIMPLE_DEV_PM_OPS and define the required pm_ops callback
>        functions
>
> changes in v4:
>      - Moved document and section details from the commit message as comment in code.
>
> changes in v3:
>      - Corrected the typo's
>      - Updated the CC list as suggested.
>      - Corrected the format specifiers in the logs.
>
> changes in v2:
>      - Updated the commit message with test steps and logs.
>      - Added logs to include the timeout message.
>      - Fixed a potential race condition during suspend and resume.
>
>  drivers/bluetooth/btintel_pcie.c | 90 ++++++++++++++++++++++++++++++++
>  1 file changed, 90 insertions(+)
>
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 6e7bbbd35279..c419521493fe 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
> @@ -2573,11 +2573,101 @@ static void btintel_pcie_coredump(struct device *dev)
>  }
>  #endif
>
> +#ifdef CONFIG_PM_SLEEP
> +static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
> +{
> +       struct pci_dev *pdev = to_pci_dev(dev);
> +       struct btintel_pcie_data *data;
> +       ktime_t start;
> +       u32 dxstate;
> +       s64 delta_us;
> +       int err;
> +
> +       data = pci_get_drvdata(pdev);
> +
> +       dxstate = (mesg.event == PM_EVENT_SUSPEND ?
> +                  BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
> +
> +       data->gp0_received = false;
> +
> +       start = ktime_get();
> +
> +       /* Refer: 6.4.11.7 -> Platform power management */
> +       btintel_pcie_wr_sleep_cntrl(data, dxstate);
> +       err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
> +                                msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
> +       if (err == 0) {
> +               bt_dev_err(data->hdev, "Timeout (%u ms) on alive interrupt for D3 entry",
> +                               BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
> +               return -EBUSY;
> +       }
> +
> +       delta_us = ktime_to_ns(ktime_get() - start) / 1000;
> +       bt_dev_info(data->hdev, "device entered into d3 state from d0 in %lld us",
> +                   delta_us);
> +       return 0;
> +}
> +
> +static int btintel_pcie_suspend(struct device *dev)
> +{
> +       return btintel_pcie_suspend_late(dev, PMSG_SUSPEND);
> +}
> +
> +static int btintel_pcie_hibernate(struct device *dev)
> +{
> +       return btintel_pcie_suspend_late(dev, PMSG_HIBERNATE);
> +}
> +
> +static int btintel_pcie_freeze(struct device *dev)
> +{
> +       return btintel_pcie_suspend_late(dev, PMSG_FREEZE);
> +}
> +
> +static int btintel_pcie_resume(struct device *dev)
> +{
> +       struct pci_dev *pdev = to_pci_dev(dev);
> +       struct btintel_pcie_data *data;
> +       ktime_t start;
> +       int err;
> +       s64 delta_us;
> +
> +       data = pci_get_drvdata(pdev);
> +       data->gp0_received = false;
> +
> +       start = ktime_get();
> +
> +       /* Refer: 6.4.11.7 -> Platform power management */
> +       btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
> +       err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
> +                                msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
> +       if (err == 0) {
> +               bt_dev_err(data->hdev, "Timeout (%u ms) on alive interrupt for D0 entry",
> +                               BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
> +               return -EBUSY;
> +       }
> +
> +       delta_us = ktime_to_ns(ktime_get() - start) / 1000;
> +       bt_dev_info(data->hdev, "device entered into d0 state from d3 in %lld us",
> +                   delta_us);
> +       return 0;
> +}
> +
> +const struct dev_pm_ops btintel_pcie_pm_ops = {
> +       .suspend = pm_sleep_ptr(btintel_pcie_suspend),
> +       .resume = pm_sleep_ptr(btintel_pcie_resume),
> +       .freeze = pm_sleep_ptr(btintel_pcie_freeze),
> +       .thaw = pm_sleep_ptr(btintel_pcie_resume),
> +       .poweroff = pm_sleep_ptr(btintel_pcie_hibernate),
> +       .restore = pm_sleep_ptr(btintel_pcie_resume),
> +};
> +#endif
> +
>  static struct pci_driver btintel_pcie_driver = {
>         .name = KBUILD_MODNAME,
>         .id_table = btintel_pcie_table,
>         .probe = btintel_pcie_probe,
>         .remove = btintel_pcie_remove,
> +       .driver.pm = pm_ptr(&btintel_pcie_pm_ops),

This doesn't seem quite right, btintel_pcie_pm_ops is behind
CONFIG_PM_SLEEP not just CONFIG_PM, so it would be undefined if just
CONFIG_PM is set, so we might as well do:

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 5b32f5a6b0b0..2f1b1be94080 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -2654,12 +2654,12 @@ static int btintel_pcie_resume(struct device *dev)
 }

 const struct dev_pm_ops btintel_pcie_pm_ops = {
-       .suspend = pm_sleep_ptr(btintel_pcie_suspend),
-       .resume = pm_sleep_ptr(btintel_pcie_resume),
-       .freeze = pm_sleep_ptr(btintel_pcie_freeze),
-       .thaw = pm_sleep_ptr(btintel_pcie_resume),
-       .poweroff = pm_sleep_ptr(btintel_pcie_hibernate),
-       .restore = pm_sleep_ptr(btintel_pcie_resume),
+       .suspend = btintel_pcie_suspend,
+       .resume = btintel_pcie_resume,
+       .freeze = btintel_pcie_freeze,
+       .thaw = btintel_pcie_resume,
+       .poweroff = btintel_pcie_hibernate,
+       .restore = btintel_pcie_resume,
 };
 #endif

@@ -2668,7 +2668,7 @@ static struct pci_driver btintel_pcie_driver = {
        .id_table = btintel_pcie_table,
        .probe = btintel_pcie_probe,
        .remove = btintel_pcie_remove,
-       .driver.pm = pm_ptr(&btintel_pcie_pm_ops),
+       .driver.pm = pm_sleep_ptr(&btintel_pcie_pm_ops),
 #ifdef CONFIG_DEV_COREDUMP
        .driver.coredump = btintel_pcie_coredump
 #endif


>  #ifdef CONFIG_DEV_COREDUMP
>         .driver.coredump = btintel_pcie_coredump
>  #endif
> --
> 2.43.0
>
>


-- 
Luiz Augusto von Dentz





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux