From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 471AF4F3930; Mon, 28 Sep 2026 17:07:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.7 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790615259; cv=none; b=d39jZpz6W26bjiomMflNXGUr/y+9nbk7wPVcinpNzpsXHaG3PxHxQ+ikw+0q7A84He6Tdks3EqyoRsnqpgFExwhMHV+YjSScwWiyGJSM/8IFDgdQVPunzZ5KqksJUKsZhH4EBeu65VobF5zfLD0ce6mq8M5F5j5hjBwaC+CZHnw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790615259; c=relaxed/simple; bh=PCzjwzA1kfNsyIJ8Ntvjws0h7EFX9dydBuT/We6VlF0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kCuS1KNAtNaBh6Zq2AnXC48mkpWEjGlsn+Ht8NNpyc05uJ8E4QjuTMRuy3DwS8VaJY5J/FHjBHDmfmcYJSYcPywWrVzZIgIQSyj3n6NU/sSBDqLAT+1of/07e+ETTu4L2SO1Aj/Y4h+goa0FIV7j59F+PBWjmkNj7Xm6fmoWup4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=hOiOh4kT; arc=none smtp.client-ip=192.198.163.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="hOiOh4kT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1790615255; x=1822151255; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=PCzjwzA1kfNsyIJ8Ntvjws0h7EFX9dydBuT/We6VlF0=; b=hOiOh4kTGQzmiTd6cuiQVMs/U7xANHnTvnpJ9nkfxLQ+Aj/lk+t6f2Wj ghFYgs9EpNqrk8eWEmVGdkdjJYl1FJi9zqza3IPgkYKstNm42FiRfobPM ZfgNxRumutLvBPkbNDaoq06+sxHB+NO4dU4R3PyDoOAxsooiDx7CJrkuh 0o4SDZaPY3W2AsOnifbjI/W62JI8plZdCDo9GfS84zjXBrmvztzJ54Dyo MvlWM0wtrw2tSogTBXYvjsk4amQmw0ZpMjW+V6bMmoB7QM+vQGJMKP/+V 2khxTOnr1/0K6WNEoVoTp8IAZmHQ2ySGad8xxn6mAwLT7LOumrh6H9S1I g==; X-CSE-ConnectionGUID: vfW1zUYpQ+yGd7HwruCetw== X-CSE-MsgGUID: Lr9ccG+tSa6NSKVH9DVwGg== X-IronPort-AV: E=McAfee;i="6800,10657,11919"; a="116847872" X-IronPort-AV: E=Sophos;i="6.27,128,1787036400"; d="scan'208";a="116847872" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by fmvoesa101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Sep 2026 10:07:34 -0700 X-CSE-ConnectionGUID: v7eqUS2OSWqc9mh2ydS/QQ== X-CSE-MsgGUID: 8LTPLEKKRkyDUkICQg9dGA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.27,128,1787036400"; d="scan'208";a="275222330" Received: from unknown (HELO WEBA2062.iind.intel.com) ([10.66.228.2]) by fmviesa008.fm.intel.com with ESMTP; 28 Sep 2026 10:07:32 -0700 From: Ravindra To: linux-bluetooth@vger.kernel.org Cc: lsa.uz@pm.me, pmenzel@molgen.mpg.de, marcel@holtmann.org, luiz.dentz@gmail.com, kiran.k@intel.com, chethan.tumkur.narayan@intel.com, linux-kernel@vger.kernel.org, Ravindra Subject: [PATCH v6 2/4] Bluetooth: btintel_pcie: fix PM flow for S0ix, S3 and S4 Date: Mon, 28 Sep 2026 22:40:01 +0530 Message-ID: <20260928171003.2925480-3-ravindra@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260928171003.2925480-1-ravindra@intel.com> References: <20260928171003.2925480-1-ravindra@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Fix two issues in the PM suspend/resume path: 1. S3 was handled the same as S0ix, keeping the controller on the D3hot-style path. That caused resume instability because S3 can remove power from the controller, unlike s2idle/S0ix. Use pm_suspend_target_state to distinguish them: D3_HOT for S0ix and D3_COLD for S3/S4. Add .restore to force FLR-based firmware recovery after S4 and S3 (PM_SUSPEND_MEM), as power is lost. S0ix resumes via a normal D0 transition. 2. During hibernation, .freeze() puts the controller into the D3cold state without any loss of power, and the flow normally continues to .poweroff(). If hibernation instead fails, .thaw() is called to bring the controller back up, and the old code routed it through btintel_pcie_resume(), which forced FLR-based firmware recovery whenever data->pm_sx_event was PM_EVENT_FREEZE. That check was incorrect: since the controller's power was never actually removed on this failed-hibernation path, FLR-based recovery is unnecessary. Remove pm_sx_event and route .thaw through a normal D0 transition instead; FLR-based recovery is retained only in .restore, where genuine S4 power loss requires it. Tested with: S0ix: sudo sh -c 'echo "+40" > /sys/class/rtc/rtc0/wakealarm' && \ echo freeze | sudo tee /sys/power/state S3: sudo rtcwake -m mem -s 60 S4: sudo rtcwake -m disk -s 60 Fixes: e57362f4911b ("Bluetooth: btintel_pcie: Add support for _suspend() / _resume()") Assisted-by: GitHub-Copilot:GPT5 Signed-off-by: Ravindra Tested-by: Sergey Lebedev Reviewed-by: Paul Menzel --- drivers/bluetooth/btintel_pcie.c | 58 +++++++++++++++++++++----------- drivers/bluetooth/btintel_pcie.h | 2 -- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 677749903173..082f5ec8ba71 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -4199,7 +4200,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 { @@ -4251,18 +4253,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", @@ -4285,7 +4292,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; @@ -4293,19 +4300,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; @@ -4314,7 +4317,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)); @@ -4339,13 +4344,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 index 016795fcbccc..3030b4e8b750 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -713,7 +713,6 @@ struct btintel_pcie_ini_dump_info { * @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; @@ -773,7 +772,6 @@ struct btintel_pcie_data { struct btintel_pcie_dbgc dbgc; struct btintel_pcie_mdbgc mdbgc; struct btintel_pcie_dump_header dmp_hdr; - u8 pm_sx_event; u32 debug_evt_addr; u32 debug_evt_size; dma_addr_t debug_table_addr; -- 2.43.0