From 513f49c33e91e58975ada7967b44512179f0e703 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 7 May 2026 13:29:41 +0800 Subject: [PATCH 1/8] power: sequencing: print power sequencing device parent in debugfs The debugfs summary currently shows the power sequencing device's name. This is not really helpful since the device name is always "pwrseq.N". Also print the parent device's name. This would likely be the device node name from the device tree, something like "nvme-connector". This would make it much easier for the developer to associate the summary with a certain device. Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260507052943.3133349-1-wenst@chromium.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 4dff71be11b6..14335c4f813e 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -1043,7 +1043,7 @@ static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data) struct pwrseq_target *target; struct pwrseq_unit *unit; - seq_printf(seq, "%s:\n", dev_name(dev)); + seq_printf(seq, "%s (%s):\n", dev_name(dev), dev_name(dev->parent)); seq_puts(seq, " targets:\n"); list_for_each_entry(target, &pwrseq->targets, list) From da35b212f8cfd2fb2b7beaf46f4ae758f29c4ea7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 14:25:56 +0530 Subject: [PATCH 2/8] power: sequencing: pcie-m2: Fix inconsistent function prefixes All functions in this driver follow 'pwrseq_pcie_m2' prefix except a few. Fix them to avoid inconsistency. Tested-by: Wei Deng Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260519-pwrseq-m2-bt-v3-1-b39dc2ae3966@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index ef69ae268059..b2ed336fd5ad 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -177,7 +177,7 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, return PWRSEQ_NO_MATCH; } -static int pwrseq_m2_pcie_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, +static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, struct device_node *parent) { struct device *dev = ctx->dev; @@ -254,7 +254,7 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) goto err_put_ctrl; } - ret = pwrseq_m2_pcie_create_bt_node(ctx, serdev_parent); + ret = pwrseq_pcie_m2_create_bt_node(ctx, serdev_parent); if (ret) goto err_free_serdev; @@ -299,7 +299,7 @@ static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx) } } -static int pwrseq_m2_pcie_notify(struct notifier_block *nb, unsigned long action, +static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action, void *data) { struct pwrseq_pcie_m2_ctx *ctx = container_of(nb, struct pwrseq_pcie_m2_ctx, nb); @@ -364,7 +364,7 @@ static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx, stru if (pwrseq_pcie_m2_check_remote_node(dev, 3, 0, "serial")) { if (pwrseq_pcie_m2_check_remote_node(dev, 0, 0, "pcie")) { ctx->dev = dev; - ctx->nb.notifier_call = pwrseq_m2_pcie_notify; + ctx->nb.notifier_call = pwrseq_pcie_m2_notify; ret = bus_register_notifier(&pci_bus_type, &ctx->nb); if (ret) return dev_err_probe(dev, ret, From 0822425512762d492d7d1500a38d8d519a2b96fe Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 14:25:57 +0530 Subject: [PATCH 3/8] power: sequencing: pcie-m2: Allow creating serdev for multiple PCI devices Current code makes it possible to create serdev for only one PCI device. But for scaling this driver, it is necessary to allow creating serdev for multiple PCI devices. Hence, add provision for it by creating 'struct pwrseq_pci_dev' for each PCI device that requires serdev and add them to 'pwrseq_pcie_m2_ctx::pci_devices' list. Tested-by: Wei Deng Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260519-pwrseq-m2-bt-v3-2-b39dc2ae3966@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 127 +++++++++++++++------- 1 file changed, 88 insertions(+), 39 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index b2ed336fd5ad..469e130330fa 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,13 @@ #include #include +struct pwrseq_pci_dev { + struct serdev_device *serdev; + struct of_changeset *ocs; + struct pci_dev *pdev; + struct list_head list; +}; + struct pwrseq_pcie_m2_pdata { const struct pwrseq_target_data **targets; }; @@ -32,9 +40,9 @@ struct pwrseq_pcie_m2_ctx { struct notifier_block nb; struct gpio_desc *w_disable1_gpio; struct gpio_desc *w_disable2_gpio; - struct serdev_device *serdev; - struct of_changeset *ocs; struct device *dev; + struct list_head pci_devices; + struct mutex list_lock; }; static int pwrseq_pcie_m2_vregs_enable(struct pwrseq_device *pwrseq) @@ -178,38 +186,39 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, } static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, + struct pwrseq_pci_dev *pci_dev, struct device_node *parent) { struct device *dev = ctx->dev; struct device_node *np; int ret; - ctx->ocs = kzalloc_obj(*ctx->ocs); - if (!ctx->ocs) + pci_dev->ocs = kzalloc_obj(*pci_dev->ocs); + if (!pci_dev->ocs) return -ENOMEM; - of_changeset_init(ctx->ocs); + of_changeset_init(pci_dev->ocs); - np = of_changeset_create_node(ctx->ocs, parent, "bluetooth"); + np = of_changeset_create_node(pci_dev->ocs, parent, "bluetooth"); if (!np) { dev_err(dev, "Failed to create bluetooth node\n"); ret = -ENODEV; goto err_destroy_changeset; } - ret = of_changeset_add_prop_string(ctx->ocs, np, "compatible", "qcom,wcn7850-bt"); + ret = of_changeset_add_prop_string(pci_dev->ocs, np, "compatible", "qcom,wcn7850-bt"); if (ret) { dev_err(dev, "Failed to add bluetooth compatible: %d\n", ret); goto err_destroy_changeset; } - ret = of_changeset_apply(ctx->ocs); + ret = of_changeset_apply(pci_dev->ocs); if (ret) { dev_err(dev, "Failed to apply changeset: %d\n", ret); goto err_destroy_changeset; } - ret = device_add_of_node(&ctx->serdev->dev, np); + ret = device_add_of_node(&pci_dev->serdev->dev, np); if (ret) { dev_err(dev, "Failed to add OF node: %d\n", ret); goto err_revert_changeset; @@ -218,19 +227,21 @@ static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, return 0; err_revert_changeset: - of_changeset_revert(ctx->ocs); + of_changeset_revert(pci_dev->ocs); err_destroy_changeset: - of_changeset_destroy(ctx->ocs); - kfree(ctx->ocs); - ctx->ocs = NULL; + of_changeset_destroy(pci_dev->ocs); + kfree(pci_dev->ocs); + pci_dev->ocs = NULL; return ret; } -static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) +static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx, + struct pci_dev *pdev) { struct serdev_controller *serdev_ctrl; struct device *dev = ctx->dev; + struct pwrseq_pci_dev *pci_dev; int ret; struct device_node *serdev_parent __free(device_node) = @@ -248,17 +259,23 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) return 0; } - ctx->serdev = serdev_device_alloc(serdev_ctrl); - if (!ctx->serdev) { + pci_dev = kzalloc(sizeof(*pci_dev), GFP_KERNEL); + if (!pci_dev) { ret = -ENOMEM; goto err_put_ctrl; } - ret = pwrseq_pcie_m2_create_bt_node(ctx, serdev_parent); + pci_dev->serdev = serdev_device_alloc(serdev_ctrl); + if (!pci_dev->serdev) { + ret = -ENOMEM; + goto err_free_pci_dev; + } + + ret = pwrseq_pcie_m2_create_bt_node(ctx, pci_dev, serdev_parent); if (ret) goto err_free_serdev; - ret = serdev_device_add(ctx->serdev); + ret = serdev_device_add(pci_dev->serdev); if (ret) { dev_err(dev, "Failed to add serdev for WCN7850: %d\n", ret); goto err_free_dt_node; @@ -266,37 +283,64 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) serdev_controller_put(serdev_ctrl); + pci_dev->pdev = pci_dev_get(pdev); + + mutex_lock(&ctx->list_lock); + list_add_tail(&pci_dev->list, &ctx->pci_devices); + mutex_unlock(&ctx->list_lock); + return 0; err_free_dt_node: - device_remove_of_node(&ctx->serdev->dev); - of_changeset_revert(ctx->ocs); - of_changeset_destroy(ctx->ocs); - kfree(ctx->ocs); - ctx->ocs = NULL; + device_remove_of_node(&pci_dev->serdev->dev); + of_changeset_revert(pci_dev->ocs); + of_changeset_destroy(pci_dev->ocs); + kfree(pci_dev->ocs); + pci_dev->ocs = NULL; err_free_serdev: - serdev_device_put(ctx->serdev); - ctx->serdev = NULL; + serdev_device_put(pci_dev->serdev); + pci_dev->serdev = NULL; +err_free_pci_dev: + kfree(pci_dev); err_put_ctrl: serdev_controller_put(serdev_ctrl); return ret; } -static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx) +static void __pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx, + struct pwrseq_pci_dev *pci_dev) { - if (ctx->serdev) { - device_remove_of_node(&ctx->serdev->dev); - serdev_device_remove(ctx->serdev); - ctx->serdev = NULL; + if (pci_dev->serdev) { + device_remove_of_node(&pci_dev->serdev->dev); + serdev_device_remove(pci_dev->serdev); } - if (ctx->ocs) { - of_changeset_revert(ctx->ocs); - of_changeset_destroy(ctx->ocs); - kfree(ctx->ocs); - ctx->ocs = NULL; + if (pci_dev->ocs) { + of_changeset_revert(pci_dev->ocs); + of_changeset_destroy(pci_dev->ocs); + kfree(pci_dev->ocs); } + + pci_dev_put(pci_dev->pdev); + list_del(&pci_dev->list); + kfree(pci_dev); +} + +static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx, + struct pci_dev *pdev) +{ + struct pwrseq_pci_dev *pci_dev, *tmp; + + mutex_lock(&ctx->list_lock); + list_for_each_entry_safe(pci_dev, tmp, &ctx->pci_devices, list) { + if (!pdev || pci_dev->pdev == pdev) { + __pwrseq_pcie_m2_remove_serdev(ctx, pci_dev); + if (pdev) + break; + } + } + mutex_unlock(&ctx->list_lock); } static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action, @@ -320,7 +364,7 @@ static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action case BUS_NOTIFY_ADD_DEVICE: /* Create serdev device for WCN7850 */ if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) { - ret = pwrseq_pcie_m2_create_serdev(ctx); + ret = pwrseq_pcie_m2_create_serdev(ctx, pdev); if (ret) return notifier_from_errno(ret); } @@ -328,7 +372,7 @@ static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action case BUS_NOTIFY_REMOVED_DEVICE: /* Destroy serdev device for WCN7850 */ if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) - pwrseq_pcie_m2_remove_serdev(ctx); + pwrseq_pcie_m2_remove_serdev(ctx, pdev); break; } @@ -432,16 +476,20 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev) goto err_free_regulators; } + mutex_init(&ctx->list_lock); + INIT_LIST_HEAD(&ctx->pci_devices); /* * Register a notifier for creating protocol devices for * non-discoverable busses like UART. */ ret = pwrseq_pcie_m2_register_notifier(ctx, dev); if (ret) - goto err_free_regulators; + goto err_destroy_mutex; return 0; +err_destroy_mutex: + mutex_destroy(&ctx->list_lock); err_free_regulators: regulator_bulk_free(ctx->num_vregs, ctx->regs); @@ -453,7 +501,8 @@ static void pwrseq_pcie_m2_remove(struct platform_device *pdev) struct pwrseq_pcie_m2_ctx *ctx = platform_get_drvdata(pdev); bus_unregister_notifier(&pci_bus_type, &ctx->nb); - pwrseq_pcie_m2_remove_serdev(ctx); + pwrseq_pcie_m2_remove_serdev(ctx, NULL); + mutex_destroy(&ctx->list_lock); regulator_bulk_free(ctx->num_vregs, ctx->regs); } From 8ababf888685c3167f49c77bcfae6f82bf661be6 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 14:25:58 +0530 Subject: [PATCH 4/8] power: sequencing: pcie-m2: Improve PCI device ID check Instead of hardcoding the PCI device check, use pci_match_id() to check for the known IDs using the pwrseq_m2_pci_ids[] array. This makes adding support for new devices easier. Tested-by: Wei Deng Reviewed-by: Konrad Dybcio Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260519-pwrseq-m2-bt-v3-3-b39dc2ae3966@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index 469e130330fa..038271207a27 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -343,6 +343,11 @@ static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx, mutex_unlock(&ctx->list_lock); } +static const struct pci_device_id pwrseq_m2_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107) }, + { } /* Sentinel */ +}; + static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action, void *data) { @@ -362,16 +367,14 @@ static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action switch (action) { case BUS_NOTIFY_ADD_DEVICE: - /* Create serdev device for WCN7850 */ - if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) { + if (pci_match_id(pwrseq_m2_pci_ids, pdev)) { ret = pwrseq_pcie_m2_create_serdev(ctx, pdev); if (ret) return notifier_from_errno(ret); } break; case BUS_NOTIFY_REMOVED_DEVICE: - /* Destroy serdev device for WCN7850 */ - if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) + if (pci_match_id(pwrseq_m2_pci_ids, pdev)) pwrseq_pcie_m2_remove_serdev(ctx, pdev); break; From c4be520907a42981fd32736b6eec18aaaee738c8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 14:25:59 +0530 Subject: [PATCH 5/8] power: sequencing: pcie-m2: Create serdev for PCI devices present before probe So far, the driver is registering a notifier to create serdev for the PCI devices that are going to be attached after probe. But it doesn't handle the devices present before probe. Due to this, serdev is not getting created for those existing devices. Hence, create serdev for PCI devices available before probe as well. Note that the serdev for available devices are created before registering the notifier. There is a small window where a device could appear after pwrseq_pcie_m2_create_serdev(), before notifier registration. But since M.2 cards are fixed to a slot, they are mostly added either before booting the host or after using hotplug. So this window is mostly theoretical. Tested-by: Wei Deng Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260519-pwrseq-m2-bt-v3-4-b39dc2ae3966@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 81 +++++++++++++++++++---- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index 038271207a27..8164c4428977 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -236,7 +236,7 @@ static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, return ret; } -static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx, +static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx, struct pci_dev *pdev) { struct serdev_controller *serdev_ctrl; @@ -259,6 +259,14 @@ static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx, return 0; } + /* Bail out if the serdev device was already created for the PCI dev */ + scoped_guard(mutex, &ctx->list_lock) { + list_for_each_entry(pci_dev, &ctx->pci_devices, list) { + if (pci_dev->pdev == pdev) + return 0; + } + } + pci_dev = kzalloc(sizeof(*pci_dev), GFP_KERNEL); if (!pci_dev) { ret = -ENOMEM; @@ -368,7 +376,7 @@ static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action switch (action) { case BUS_NOTIFY_ADD_DEVICE: if (pci_match_id(pwrseq_m2_pci_ids, pdev)) { - ret = pwrseq_pcie_m2_create_serdev(ctx, pdev); + ret = pwrseq_pcie_m2_create_serdev_one(ctx, pdev); if (ret) return notifier_from_errno(ret); } @@ -400,7 +408,7 @@ static bool pwrseq_pcie_m2_check_remote_node(struct device *dev, u8 port, u8 end * protocol device needs to be created manually with the help of the notifier * of the discoverable bus like PCIe. */ -static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx, struct device *dev) +static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx) { int ret; @@ -408,18 +416,56 @@ static int pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx, stru * Register a PCI notifier for Key E connector that has PCIe as Port * 0/Endpoint 0 interface and Serial as Port 3/Endpoint 0 interface. */ - if (pwrseq_pcie_m2_check_remote_node(dev, 3, 0, "serial")) { - if (pwrseq_pcie_m2_check_remote_node(dev, 0, 0, "pcie")) { - ctx->dev = dev; - ctx->nb.notifier_call = pwrseq_pcie_m2_notify; - ret = bus_register_notifier(&pci_bus_type, &ctx->nb); - if (ret) - return dev_err_probe(dev, ret, - "Failed to register notifier for serdev\n"); + if (!pwrseq_pcie_m2_check_remote_node(ctx->dev, 3, 0, "serial") || + !pwrseq_pcie_m2_check_remote_node(ctx->dev, 0, 0, "pcie")) + return 0; + + ctx->nb.notifier_call = pwrseq_pcie_m2_notify; + ret = bus_register_notifier(&pci_bus_type, &ctx->nb); + if (ret) + return dev_err_probe(ctx->dev, ret, + "Failed to register notifier for serdev\n"); + return 0; +} + +static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx) +{ + struct pci_dev *pdev = NULL; + int ret; + + if (!pwrseq_pcie_m2_check_remote_node(ctx->dev, 3, 0, "serial") || + !pwrseq_pcie_m2_check_remote_node(ctx->dev, 0, 0, "pcie")) + return 0; + + struct device_node *pci_parent __free(device_node) = + of_graph_get_remote_node(dev_of_node(ctx->dev), 0, 0); + if (!pci_parent) + return 0; + + /* Create serdev for existing PCI devices if required */ + for_each_pci_dev(pdev) { + 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; } } return 0; + +err_remove_serdev: + pwrseq_pcie_m2_remove_serdev(ctx, NULL); + + return ret; } static int pwrseq_pcie_m2_probe(struct platform_device *pdev) @@ -481,16 +527,25 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev) mutex_init(&ctx->list_lock); INIT_LIST_HEAD(&ctx->pci_devices); + ctx->dev = dev; + + /* Create serdev for available PCI devices (if required) */ + ret = pwrseq_pcie_m2_create_serdev(ctx); + if (ret) + goto err_destroy_mutex; + /* * Register a notifier for creating protocol devices for * non-discoverable busses like UART. */ - ret = pwrseq_pcie_m2_register_notifier(ctx, dev); + ret = pwrseq_pcie_m2_register_notifier(ctx); if (ret) - goto err_destroy_mutex; + goto err_remove_serdev; return 0; +err_remove_serdev: + pwrseq_pcie_m2_remove_serdev(ctx, NULL); err_destroy_mutex: mutex_destroy(&ctx->list_lock); err_free_regulators: From f2aeaa6d47d3e94285b6ff648b929f3eeb5ce600 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 14:26:00 +0530 Subject: [PATCH 6/8] power: sequencing: pcie-m2: Create BT node based on the pci_device_id[] table Currently, pwrseq_pcie_m2_create_bt_node() hardcodes the BT compatible for creating the devicetree node. But to allow adding support for more devices in the future, create the BT node based on the pci_device_id[] table. The BT compatible is passed using 'driver_data'. Co-developed-by: Wei Deng Signed-off-by: Wei Deng Tested-by: Wei Deng Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260519-pwrseq-m2-bt-v3-5-b39dc2ae3966@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 29 ++++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index 8164c4428977..e82821655fc4 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -185,14 +185,29 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, return PWRSEQ_NO_MATCH; } +static const struct pci_device_id pwrseq_m2_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107), + .driver_data = (kernel_ulong_t)"qcom,wcn7850-bt" }, + { } /* Sentinel */ +}; + static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, struct pwrseq_pci_dev *pci_dev, - struct device_node *parent) + struct device_node *parent, + struct pci_dev *pdev) { + const struct pci_device_id *id; struct device *dev = ctx->dev; + const char *compatible; struct device_node *np; int ret; + id = pci_match_id(pwrseq_m2_pci_ids, pdev); + if (WARN_ON_ONCE(!id)) /* Shouldn't happen */ + return -ENODEV; + + compatible = (const char *)id->driver_data; + pci_dev->ocs = kzalloc_obj(*pci_dev->ocs); if (!pci_dev->ocs) return -ENOMEM; @@ -206,7 +221,7 @@ static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx, goto err_destroy_changeset; } - ret = of_changeset_add_prop_string(pci_dev->ocs, np, "compatible", "qcom,wcn7850-bt"); + ret = of_changeset_add_prop_string(pci_dev->ocs, np, "compatible", compatible); if (ret) { dev_err(dev, "Failed to add bluetooth compatible: %d\n", ret); goto err_destroy_changeset; @@ -279,13 +294,14 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx, goto err_free_pci_dev; } - ret = pwrseq_pcie_m2_create_bt_node(ctx, pci_dev, serdev_parent); + ret = pwrseq_pcie_m2_create_bt_node(ctx, pci_dev, serdev_parent, pdev); if (ret) goto err_free_serdev; ret = serdev_device_add(pci_dev->serdev); if (ret) { - dev_err(dev, "Failed to add serdev for WCN7850: %d\n", ret); + dev_err(dev, "Failed to add serdev for PCI device (%s): %d\n", + pci_name(pdev), ret); goto err_free_dt_node; } @@ -351,11 +367,6 @@ static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx, mutex_unlock(&ctx->list_lock); } -static const struct pci_device_id pwrseq_m2_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107) }, - { } /* Sentinel */ -}; - static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action, void *data) { From 9085f71285375485c86c06071a13111606b404d7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 May 2026 14:26:01 +0530 Subject: [PATCH 7/8] power: sequencing: Add an API to return the pwrseq device's 'dev' pointer The consumer drivers can make use of the pwrseq device's 'dev' pointer to query the pwrseq provider's DT node to check for existence of specific properties. Hence, add an API to return the pwrseq device's 'dev' pointer to consumers. Note that since pwrseq_get() would've increased the pwrseq refcount, there is no need to increase the refcount in this API again. Tested-by: Wei Deng Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260519-pwrseq-m2-bt-v3-6-b39dc2ae3966@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 23 +++++++++++++++++++++++ include/linux/pwrseq/consumer.h | 7 +++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 14335c4f813e..89694092981f 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -965,6 +965,29 @@ int pwrseq_power_off(struct pwrseq_desc *desc) } EXPORT_SYMBOL_GPL(pwrseq_power_off); +/** + * pwrseq_to_device() - Get the pwrseq device pointer from a descriptor. + * @desc: Descriptor referencing the power sequencer. + * + * Return the 'dev' pointer of the power sequencer device associated with @desc. + * Consumer drivers can use this to query the pwrseq provider's device tree + * node, for example to check for the existence of specific properties. + * + * Since pwrseq_get() already takes a reference to the pwrseq device, this + * function does not take an additional reference. + * + * Returns: + * Pointer to the pwrseq struct device, or NULL if @desc is NULL. + */ +struct device *pwrseq_to_device(struct pwrseq_desc *desc) +{ + if (!desc) + return NULL; + + return &desc->pwrseq->dev; +} +EXPORT_SYMBOL_GPL(pwrseq_to_device); + #if IS_ENABLED(CONFIG_DEBUG_FS) struct pwrseq_debugfs_count_ctx { diff --git a/include/linux/pwrseq/consumer.h b/include/linux/pwrseq/consumer.h index 7d583b4f266e..3c907c9e1885 100644 --- a/include/linux/pwrseq/consumer.h +++ b/include/linux/pwrseq/consumer.h @@ -23,6 +23,8 @@ devm_pwrseq_get(struct device *dev, const char *target); int pwrseq_power_on(struct pwrseq_desc *desc); int pwrseq_power_off(struct pwrseq_desc *desc); +struct device *pwrseq_to_device(struct pwrseq_desc *desc); + #else /* CONFIG_POWER_SEQUENCING */ static inline struct pwrseq_desc * __must_check @@ -51,6 +53,11 @@ static inline int pwrseq_power_off(struct pwrseq_desc *desc) return -ENOSYS; } +static inline struct device *pwrseq_to_device(struct pwrseq_desc *desc) +{ + return NULL; +} + #endif /* CONFIG_POWER_SEQUENCING */ #endif /* __POWER_SEQUENCING_CONSUMER_H__ */ From 2abcfdd91e6acc9999c1a38a6a077835a9db380c Mon Sep 17 00:00:00 2001 From: Wei Deng Date: Mon, 8 Jun 2026 14:47:01 +0530 Subject: [PATCH 8/8] power: sequencing: pcie-m2: Add PCI ID 0x1103 for WCN6855 Bluetooth WCN6855 is a Qualcomm Wi-Fi/BT combo chip that uses PCI device ID 0x1103. Add it to pwrseq_m2_pci_ids[] alongside the existing 0x1107 (WCN7850) entry, so that the pwrseq-pcie-m2 driver creates a Bluetooth serdev device for WCN6855 cards inserted into PCIe M.2 Key E connectors. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Wei Deng Link: https://patch.msgid.link/20260608091702.3797437-2-wei.deng@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-pcie-m2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index e82821655fc4..94c3f4b7ee36 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -188,6 +188,8 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, static const struct pci_device_id pwrseq_m2_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1107), .driver_data = (kernel_ulong_t)"qcom,wcn7850-bt" }, + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x1103), + .driver_data = (kernel_ulong_t)"qcom,wcn6855-bt" }, { } /* Sentinel */ };