Dear Chandrashekar,
Thank you for your patch. Some more minor things.
Am 27.01.25 um 13:49 schrieb Chandrashekar Devegowda:
From: chandrashekar Devegowda <chandrashekar.devegowda@xxxxxxxxx>
Please capatilize the first name.
This patch contains the changes in driver for vendor specific handshake
during enter of D3 and D0 exit.
Maybe state the problem at the very beginning, and say that all Intel
Bluetooth devices do not enter D0.
Command to test host initiated wakeup after 60 seconds
sudo rtcwake -m mem -s 60
logs from testing:
Bluetooth: hci0: BT device resumed to D0 in 1016 usecs
On what device did you test this? Were you able to measure a power
consumption reduction?
I am also asking this, thinking that recent Intel devices only support
ACPI S0ix and not ACPI S3.
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@xxxxxxxxx>
Signed-off-by: Kiran K <kiran.k@xxxxxxxxx>
---
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 | 66 ++++++++++++++++++++++++++++++++
drivers/bluetooth/btintel_pcie.h | 4 ++
2 files changed, 70 insertions(+)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 63eca52c0e0b..4627544a2a52 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -274,6 +274,12 @@ static int btintel_pcie_reset_bt(struct btintel_pcie_data *data)
return reg == 0 ? 0 : -ENODEV;
}
+static void btintel_pcie_set_persistence_mode(struct btintel_pcie_data *data)
+{
+ btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_HW_BOOT_CONFIG,
+ BTINTEL_PCIE_CSR_HW_BOOT_CONFIG_KEEP_ON);
+}
+
/* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in
* BTINTEL_PCIE_CSR_FUNC_CTRL_REG register and wait for MSI-X with
* BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0.
@@ -298,6 +304,8 @@ static int btintel_pcie_enable_bt(struct btintel_pcie_data *data)
*/
data->boot_stage_cache = 0x0;
+ btintel_pcie_set_persistence_mode(data);
+
/* Set MAC_INIT bit to start primary bootloader */
reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT |
@@ -1649,11 +1657,69 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
pci_set_drvdata(pdev, NULL);
}
+static int btintel_pcie_suspend(struct device *dev)
+{
+ struct btintel_pcie_data *data;
+ int err;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ data = pci_get_drvdata(pdev);
+ data->gp0_received = false;
+ /* The implementation is as per the Intel SAS document:
+ * BT Platform Power Management SAS - IOSF and the specific sections are
+ * 3.1 D0Exit (D3 Entry) Flow.
+ */
+ btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D3_HOT);
+ err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
+ msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
+ if (!err) {
+ bt_dev_err(data->hdev, "failed to receive gp0 interrupt for suspend in %d msecs",
+ BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int btintel_pcie_resume(struct device *dev)
+{
+ struct btintel_pcie_data *data;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ ktime_t calltime, delta, rettime;
+ unsigned long long duration;
+ int err;
+
+ data = pci_get_drvdata(pdev);
+ data->gp0_received = false;
+ /* The implementation is as per the Intel SAS document:
+ * BT Platform Power Management SAS - IOSF and the specific sections are
+ * 3.2 D0Entry (D3 Exit) Flow
Missing space in D0 Entry.
+ */
+ calltime = ktime_get();
+ 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) {
+ bt_dev_err(data->hdev, "failed to receive gp0 interrupt for resume in %d msecs",
+ BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
+ return -EBUSY;
+ }
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+ bt_dev_info(data->hdev, "BT device resumed to D0 in %llu usecs", duration);
Maybe also add *from D3*.
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(btintel_pcie_pm_ops, btintel_pcie_suspend,
+ btintel_pcie_resume);
+
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 = &btintel_pcie_pm_ops,
};
module_pci_driver(btintel_pcie_driver);
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index f9aada0543c4..38d0c8ea2b6f 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -8,6 +8,7 @@
/* Control and Status Register(BTINTEL_PCIE_CSR) */
#define BTINTEL_PCIE_CSR_BASE (0x000)
+#define BTINTEL_PCIE_CSR_HW_BOOT_CONFIG (BTINTEL_PCIE_CSR_BASE + 0x000)
#define BTINTEL_PCIE_CSR_FUNC_CTRL_REG (BTINTEL_PCIE_CSR_BASE + 0x024)
#define BTINTEL_PCIE_CSR_HW_REV_REG (BTINTEL_PCIE_CSR_BASE + 0x028)
#define BTINTEL_PCIE_CSR_RF_ID_REG (BTINTEL_PCIE_CSR_BASE + 0x09C)
@@ -48,6 +49,9 @@
#define BTINTEL_PCIE_CSR_MSIX_IVAR_BASE (BTINTEL_PCIE_CSR_MSIX_BASE + 0x0880)
#define BTINTEL_PCIE_CSR_MSIX_IVAR(cause) (BTINTEL_PCIE_CSR_MSIX_IVAR_BASE + (cause))
+/* CSR HW BOOT CONFIG Register */
+#define BTINTEL_PCIE_CSR_HW_BOOT_CONFIG_KEEP_ON (BIT(31))
+
/* Causes for the FH register interrupts */
enum msix_fh_int_causes {
BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0 = BIT(0), /* cause 0 */
Kind regards,
Paul