Re: [PATCH v6 9/9] power: sequencing: pcie-m2: Create serdev device for WCN7850 bluetooth
From: Manivannan Sadhasivam
Date: Tue Mar 17 2026 - 02:44:30 EST
On Tue, Mar 17, 2026 at 09:59:59AM +0530, Manivannan Sadhasivam via B4 Relay wrote:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxxxxxxxx>
>
> For supporting bluetooth over the non-discoverable UART interface of
> WCN7850, create the serdev device after enumerating the PCIe interface.
> This is mandatory since the device ID is only known after the PCIe
> enumeration and the ID is used for creating the serdev device.
>
> Since by default there is no OF or ACPI node for the created serdev,
> create a dynamic OF 'bluetooth' node with the 'compatible' property and
> attach it to the serdev device. This will allow the serdev device to bind
> to the existing bluetooth driver.
>
> Tested-by: Hans de Goede <johannes.goede@xxxxxxxxxxxxxxxx> # ThinkPad T14s gen6 (arm64)
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxxxxxxxx>
> ---
> drivers/power/sequencing/Kconfig | 3 +-
> drivers/power/sequencing/pwrseq-pcie-m2.c | 178 +++++++++++++++++++++++++++++-
> 2 files changed, 177 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig
> index f5fff84566ba..55aeef125e6f 100644
> --- a/drivers/power/sequencing/Kconfig
> +++ b/drivers/power/sequencing/Kconfig
> @@ -37,7 +37,8 @@ config POWER_SEQUENCING_TH1520_GPU
>
> config POWER_SEQUENCING_PCIE_M2
> tristate "PCIe M.2 connector power sequencing driver"
> - depends on OF || COMPILE_TEST
> + depends on (PCI && OF) || COMPILE_TEST
> + select OF_DYNAMIC
> help
> Say Y here to enable the power sequencing driver for PCIe M.2
> connectors. This driver handles the power sequencing for the M.2
> diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
> index 3507cdcb1e7b..77357439ba81 100644
> --- a/drivers/power/sequencing/pwrseq-pcie-m2.c
> +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
> @@ -12,9 +12,11 @@
> #include <linux/of.h>
> #include <linux/of_graph.h>
> #include <linux/of_platform.h>
> +#include <linux/pci.h>
> #include <linux/platform_device.h>
> #include <linux/pwrseq/provider.h>
> #include <linux/regulator/consumer.h>
> +#include <linux/serdev.h>
> #include <linux/slab.h>
>
> struct pwrseq_pcie_m2_pdata {
> @@ -30,6 +32,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;
> };
>
> static int pwrseq_pcie_m2_vregs_enable(struct pwrseq_device *pwrseq)
> @@ -172,13 +177,176 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
> return PWRSEQ_NO_MATCH;
> }
>
> -static void pwrseq_pcie_m2_free_regulators(void *data)
> +static void pwrseq_pcie_m2_free_resources(void *data)
> {
> struct pwrseq_pcie_m2_ctx *ctx = data;
>
> + serdev_device_remove(ctx->serdev);
> + bus_unregister_notifier(&pci_bus_type, &ctx->nb);
> + of_changeset_revert(ctx->ocs);
> + of_changeset_destroy(ctx->ocs);
> regulator_bulk_free(ctx->num_vregs, ctx->regs);
> }
>
> +static int pwrseq_m2_pcie_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx,
> + struct device_node *parent)
> +{
> + struct device *dev = ctx->dev;
> + struct device_node *np;
> + int ret;
> +
> + ctx->ocs = devm_kzalloc(dev, sizeof(*ctx->ocs), GFP_KERNEL);
> + if (!ctx->ocs)
> + return -ENOMEM;
> +
> + of_changeset_init(ctx->ocs);
> +
> + np = of_changeset_create_node(ctx->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");
> + if (ret) {
> + dev_err(dev, "Failed to add bluetooth compatible: %d\n", ret);
> + goto err_destroy_changeset;
> + }
> +
> + ret = of_changeset_apply(ctx->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);
> + if (ret) {
> + dev_err(dev, "Failed to add OF node: %d\n", ret);
> + goto err_revert_changeset;
> + }
> +
> + return 0;
> +
> +err_revert_changeset:
> + of_changeset_revert(ctx->ocs);
> +err_destroy_changeset:
> + of_changeset_destroy(ctx->ocs);
> +
> + return ret;
> +}
> +
> +static int pwrseq_m2_pcie_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);
> + struct pci_dev *pdev = to_pci_dev(data);
> + struct serdev_controller *serdev_ctrl;
> + struct device *dev = ctx->dev;
> + int ret;
> +
> + /*
> + * Check whether the PCI device is associated with this M.2 connector or
> + * not, by comparing the OF node of the PCI device parent and the Port 0
> + * (PCIe) remote node parent OF node.
> + */
> + struct device_node *pci_parent __free(device_node) =
> + of_graph_get_remote_node(dev_of_node(ctx->dev), 0, 0);
> + if (!pci_parent || (pci_parent != pdev->dev.parent->of_node))
> + return NOTIFY_DONE;
> +
> + switch (action) {
> + case BUS_NOTIFY_ADD_DEVICE:
> + /* Create serdev device for WCN7850 */
> + if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) {
> + struct device_node *serdev_parent __free(device_node) =
> + of_graph_get_remote_node(dev_of_node(ctx->dev), 1, 1);
Typo. This should be:
of_graph_get_remote_node(dev_of_node(ctx->dev), 3, 0);
Bartosz, could you please fix it while applying?
- Mani
--
மணிவண்ணன் சதாசிவம்