[PATCH 3/3] power: sequencing: pcie-m2: Deassert W_DISABLE2# when no UART serdev is created
From: Wei Deng
Date: Thu Jul 09 2026 - 03:35:25 EST
The pwrseq_m2_pci_ids[] table lists PCIe BT devices that use UART as the
BT transport and need a UART serdev created by the driver. When a PCIe
device under the M.2 connector does not match any entry in this table,
no UART serdev is created.
However, the BT subsystem of such a device may still require W_DISABLE2#
to be deasserted to power up. Rather than adding every possible non-UART
BT device ID to the table, add an else branch that deasserts W_DISABLE2#
whenever a PCIe device is detected under the connector but does not match
a UART BT entry. This allows any BT interface on the card (USB or other)
to enumerate without requiring explicit knowledge of its device ID.
The primary use case is USB BT variants of combo chips that share the
same PCIe device ID as their UART counterpart (e.g. WCN7851 NCM865 USB,
sub 0x3378, vs NCM865A UART, sub 0x337c): no UART serdev is needed, but
W_DISABLE2# must be deasserted so the USB BT device can enumerate.
Reassert W_DISABLE2# symmetrically when the PCIe device is removed.
Validated on Hamoa EVK (IQ-X7181-EVK) with WCN7851 NCM865 USB card
(sub 0x3378): without this change GPIO116 (W_DISABLE2#) stays low and
no BT interface appears; with this change GPIO116 is driven high and the
USB BT device enumerates and comes up via btusb.
Signed-off-by: Wei Deng <wei.deng@xxxxxxxxxxxxxxxx>
---
drivers/power/sequencing/pwrseq-pcie-m2.c | 33 +++++++++++++++++++++----------
1 file changed, 23 insertions(+), 10 deletions(-)
diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
index cf51122d54fd..06eb5eb0676b 100644
--- a/drivers/power/sequencing/pwrseq-pcie-m2.c
+++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
@@ -401,11 +401,23 @@ static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action
ret = pwrseq_pcie_m2_create_serdev_one(ctx, pdev);
if (ret)
return notifier_from_errno(ret);
+ } else if (ctx->w_disable2_gpio) {
+ /*
+ * PCIe device not in the UART BT table. This covers
+ * USB BT variants of the same combo chip (same PCIe
+ * device ID, different sub-system ID, BT exposed over
+ * USB instead of UART). No UART serdev is needed, but
+ * W_DISABLE2# must be deasserted to enable the BT
+ * subsystem so the USB BT interface can enumerate.
+ */
+ gpiod_set_value_cansleep(ctx->w_disable2_gpio, 0);
}
break;
case BUS_NOTIFY_REMOVED_DEVICE:
if (pci_match_id(pwrseq_m2_pci_ids, pdev))
pwrseq_pcie_m2_remove_serdev(ctx, pdev);
+ else if (ctx->w_disable2_gpio)
+ gpiod_set_value_cansleep(ctx->w_disable2_gpio, 1);
break;
}
@@ -469,16 +481,17 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx)
if (!pdev->dev.parent || pci_parent != pdev->dev.parent->of_node)
continue;
- if (!pci_match_id(pwrseq_m2_pci_ids, pdev))
- continue;
-
- ret = pwrseq_pcie_m2_create_serdev_one(ctx, pdev);
- if (ret) {
- dev_err_probe(ctx->dev, ret,
- "Failed to create serdev for PCI device (%s)\n",
- pci_name(pdev));
- pci_dev_put(pdev);
- goto err_remove_serdev;
+ if (pci_match_id(pwrseq_m2_pci_ids, pdev)) {
+ ret = pwrseq_pcie_m2_create_serdev_one(ctx, pdev);
+ if (ret) {
+ dev_err_probe(ctx->dev, ret,
+ "Failed to create serdev for PCI device (%s)\n",
+ pci_name(pdev));
+ pci_dev_put(pdev);
+ goto err_remove_serdev;
+ }
+ } else if (ctx->w_disable2_gpio) {
+ gpiod_set_value_cansleep(ctx->w_disable2_gpio, 0);
}
}
--
2.34.1