From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 D307134252D; Wed, 12 Nov 2025 14:45:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762958732; cv=none; b=Ng4VohzEkOrhS6TEcdjvydgBsEohdeTDaMaiKfksAoZxIQ5PuPuEPIopZN+3Y3GJ/mlAoNrbAY7xUY8tIy6c3JdCaIYznXPdi8QjMcEvyMUX8HEluuqWgWP8KyWTtFmTVgEdIqHW0yKVgtl+Pwk0OxtTvKdV3KOl50jIsHepktc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762958732; c=relaxed/simple; bh=OPT3HzX6rth7Z03RPDVjbn2yRgXgLFRclo/oMJ75U+M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rvveelTFs2eg4H3XeA2YuE4E0Afmekh/lUtiJu2YTNpaqAdEKNi+9vTI28MgezLocaXtj92p9IE3ETj/HFK1vGV+3XIHwmkicyVgFF7xrpe+Wi+BzCzdrc5frVoC3T1/LCWgoVrCz8DxJKHVObsdEmZ7TfosS1BGbtjfRuvMbNE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SNthpp5V; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="SNthpp5V" Received: by smtp.kernel.org (Postfix) with ESMTPS id 856B0C2BCB3; Wed, 12 Nov 2025 14:45:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1762958731; bh=OPT3HzX6rth7Z03RPDVjbn2yRgXgLFRclo/oMJ75U+M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=SNthpp5V7cfGGwTz4anwMC0G7v25DZuquWbYqV31APrB7pQ3BgUmnB5ugNuS0US8f mBZGClhJmBSYVRAGwCmiXEBuVMghDkWo1dRS3t3LM/NyTfXpA0OET1g0phZnyfgHxT vTFJFXAexfLKgyBlv6udNJweOePTlhL7PzYdVtehdmcs88RZBWs1RoWP4KpxIsk1e/ rRU7+GUhWLy0AFaXDGEXzGDTpzbO5+0sWCX9HrArxOkCAauL6o5PozOW+a89BHIjS0 AADsQ7WB5vh7sLTWnGKwxWUuh+wfrbpg4uKVC1EGVPWhLnwlp3OZiP9sRhZedpqkDd gwckAXGvgoFSA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7EC29CCF9E3; Wed, 12 Nov 2025 14:45:31 +0000 (UTC) From: Manivannan Sadhasivam via B4 Relay Date: Wed, 12 Nov 2025 20:15:21 +0530 Subject: [PATCH 9/9] power: sequencing: pcie-m2: Add support for PCIe M.2 Key E connectors Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20251112-pci-m2-e-v1-9-97413d6bf824@oss.qualcomm.com> References: <20251112-pci-m2-e-v1-0-97413d6bf824@oss.qualcomm.com> In-Reply-To: <20251112-pci-m2-e-v1-0-97413d6bf824@oss.qualcomm.com> To: Rob Herring , Greg Kroah-Hartman , Jiri Slaby , Nathan Chancellor , Nicolas Schier , Hans de Goede , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Mark Pearson , "Derek J. Clark" , Manivannan Sadhasivam , Krzysztof Kozlowski , Conor Dooley , Marcel Holtmann , Luiz Augusto von Dentz , Bartosz Golaszewski Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org, platform-driver-x86@vger.kernel.org, linux-pci@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-bluetooth@vger.kernel.org, linux-pm@vger.kernel.org, Stephan Gerhold , Dmitry Baryshkov , Manivannan Sadhasivam X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=11328; i=manivannan.sadhasivam@oss.qualcomm.com; h=from:subject:message-id; bh=q5uSXLIpfwsDuv0DSFUAwdt87LFm61A3HFbDieiJ1GE=; b=owEBbQGS/pANAwAKAVWfEeb+kc71AcsmYgBpFJ2H95ucL4njmYyEkEHnS45DIJdhiGeRjbJqx 893431MtgmJATMEAAEKAB0WIQRnpUMqgUjL2KRYJ5dVnxHm/pHO9QUCaRSdhwAKCRBVnxHm/pHO 9XMkB/9GSOvGjKw5cEN3vSQkgcFTqgdh9f9qv29g0atX1E3CLGRm9GFbrzPOmx8UtETpuEgn2Cm N6dJaU9OcVXlXJOX8h/d3wyWQdfHA4M4LJjxHMI26Nl8X5Zn60bT4U1O4OffwR4D0Dv4+D+SxTj X9j9dEcTCD43w9ZTHXFsj8JdvzrzWeYHCjkNlZCkbVHGDrJ3at4mJSLoI6fC3lQXvRMb6jAmHRk GsnIG8bQ/SFiKQmXsMAFWVz/Zk9GsyOrn7pKo8fXwYsQppteu2sO4vvWKCdgemNUx4+twNCqVS8 Q6z5VVigobd9Q8SvkdpIVnqvAbe9v5YQ+VDbcPwkVsKANz+v X-Developer-Key: i=manivannan.sadhasivam@oss.qualcomm.com; a=openpgp; fpr=C668AEC3C3188E4C611465E7488550E901166008 X-Endpoint-Received: by B4 Relay for manivannan.sadhasivam@oss.qualcomm.com/default with auth_id=461 X-Original-From: Manivannan Sadhasivam Reply-To: manivannan.sadhasivam@oss.qualcomm.com From: Manivannan Sadhasivam Add support for handling the power sequence of the PCIe M.2 Key E connectors. These connectors are used to attach the Wireless Connectivity devices to the host machine including combinations of WiFi, BT, NFC using interfaces such as PCIe/SDIO for WiFi, USB/UART for BT and I2C for NFC. Currently, this driver supports only the PCIe interface for WiFi and UART interface for BT. The driver also only supports driving the 3.3v/1.8v power supplies and W_DISABLE{1/2}# GPIOs. The optional signals of the Key E connectors are not currently supported. For supporting Bluetooth over the non-discoverable UART interface, the driver currently creates the serdev interface 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. Signed-off-by: Manivannan Sadhasivam --- drivers/power/sequencing/Kconfig | 1 + drivers/power/sequencing/pwrseq-pcie-m2.c | 218 +++++++++++++++++++++++++++++- 2 files changed, 213 insertions(+), 6 deletions(-) diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig index f5fff84566ba463b55d3cd0c07db34c82f9f1e31..29bd204319cc1636fb424f2916d7f570af905b54 100644 --- a/drivers/power/sequencing/Kconfig +++ b/drivers/power/sequencing/Kconfig @@ -38,6 +38,7 @@ 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 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 15659b009fb3e01227e40f26d633f675bc2af701..4b5adb3f731cd5e0f69344868f88329ceba70539 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -5,14 +5,17 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include +#include #include struct pwrseq_pcie_m2_pdata { @@ -25,17 +28,20 @@ struct pwrseq_pcie_m2_ctx { const struct pwrseq_pcie_m2_pdata *pdata; struct regulator_bulk_data *regs; size_t num_vregs; + struct gpio_desc *w_disable1_gpio; + struct gpio_desc *w_disable2_gpio; struct notifier_block nb; + struct device *dev; }; -static int pwrseq_pcie_m2_m_vregs_enable(struct pwrseq_device *pwrseq) +static int pwrseq_pcie_m2_vregs_enable(struct pwrseq_device *pwrseq) { struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); return regulator_bulk_enable(ctx->num_vregs, ctx->regs); } -static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq) +static int pwrseq_pcie_m2_vregs_disable(struct pwrseq_device *pwrseq) { struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); @@ -44,18 +50,92 @@ static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq) static const struct pwrseq_unit_data pwrseq_pcie_m2_vregs_unit_data = { .name = "regulators-enable", - .enable = pwrseq_pcie_m2_m_vregs_enable, - .disable = pwrseq_pcie_m2_m_vregs_disable, + .enable = pwrseq_pcie_m2_vregs_enable, + .disable = pwrseq_pcie_m2_vregs_disable, }; -static const struct pwrseq_unit_data *pwrseq_pcie_m2_m_unit_deps[] = { +static const struct pwrseq_unit_data *pwrseq_pcie_m2_unit_deps[] = { &pwrseq_pcie_m2_vregs_unit_data, NULL }; +static int pwrseq_pci_m2_e_uart_enable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + + gpiod_set_value_cansleep(ctx->w_disable2_gpio, 0); + + return 0; +} + +static int pwrseq_pci_m2_e_uart_disable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + + gpiod_set_value_cansleep(ctx->w_disable2_gpio, 1); + + return 0; +} + +static const struct pwrseq_unit_data pwrseq_pcie_m2_e_uart_unit_data = { + .name = "uart-enable", + .deps = pwrseq_pcie_m2_unit_deps, + .enable = pwrseq_pci_m2_e_uart_enable, + .disable = pwrseq_pci_m2_e_uart_disable, +}; + +static int pwrseq_pci_m2_e_pcie_enable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + + gpiod_set_value_cansleep(ctx->w_disable1_gpio, 0); + + return 0; +} + +static int pwrseq_pci_m2_e_pcie_disable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + + gpiod_set_value_cansleep(ctx->w_disable1_gpio, 1); + + return 0; +} + +static const struct pwrseq_unit_data pwrseq_pcie_m2_e_pcie_unit_data = { + .name = "pcie-enable", + .deps = pwrseq_pcie_m2_unit_deps, + .enable = pwrseq_pci_m2_e_pcie_enable, + .disable = pwrseq_pci_m2_e_pcie_disable, +}; + static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = { .name = "pcie-enable", - .deps = pwrseq_pcie_m2_m_unit_deps, + .deps = pwrseq_pcie_m2_unit_deps, +}; + +static int pwrseq_pcie_m2_e_pwup_delay(struct pwrseq_device *pwrseq) +{ + /* + * FIXME: This delay is only required for some Qcom WLAN/BT cards like + * WCN7850 and not for all devices. But currently, there is no way to + * identify the device model before enumeration. + */ + msleep(50); + + return 0; +} + +static const struct pwrseq_target_data pwrseq_pcie_m2_e_uart_target_data = { + .name = "uart", + .unit = &pwrseq_pcie_m2_e_uart_unit_data, + .post_enable = pwrseq_pcie_m2_e_pwup_delay, +}; + +static const struct pwrseq_target_data pwrseq_pcie_m2_e_pcie_target_data = { + .name = "pcie", + .unit = &pwrseq_pcie_m2_e_pcie_unit_data, + .post_enable = pwrseq_pcie_m2_e_pwup_delay, }; static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = { @@ -63,11 +143,21 @@ static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = { .unit = &pwrseq_pcie_m2_m_pcie_unit_data, }; +static const struct pwrseq_target_data *pwrseq_pcie_m2_e_targets[] = { + &pwrseq_pcie_m2_e_pcie_target_data, + &pwrseq_pcie_m2_e_uart_target_data, + NULL +}; + static const struct pwrseq_target_data *pwrseq_pcie_m2_m_targets[] = { &pwrseq_pcie_m2_m_pcie_target_data, NULL }; +static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_e_of_data = { + .targets = pwrseq_pcie_m2_e_targets, +}; + static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_m_of_data = { .targets = pwrseq_pcie_m2_m_targets, }; @@ -95,6 +185,102 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, return PWRSEQ_NO_MATCH; } +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 device_node *remote; + struct serdev_controller *serdev_ctrl; + struct serdev_device *serdev; + 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. + */ + remote = of_graph_get_remote_node(dev_of_node(ctx->dev), 0, -1); + if (!remote || (remote != pdev->dev.parent->of_node)) { + of_node_put(remote); + return NOTIFY_DONE; + } + of_node_put(remote); + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + /* Create serdev device for WCN7850 */ + if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) { + remote = of_graph_get_remote_node(dev_of_node(ctx->dev), 1, -1); + if (!remote) { + of_node_put(remote); + return NOTIFY_DONE; + } + + serdev_ctrl = of_find_serdev_controller_by_node(remote); + of_node_put(remote); + if (!serdev_ctrl) + return NOTIFY_DONE; + + serdev = serdev_device_alloc(serdev_ctrl); + if (!serdev) + return NOTIFY_DONE; + + ret = serdev_device_add(serdev, "WCN7850"); + if (ret) { + dev_err(dev, "Failed to add serdev for WCN7850: %d\n", ret); + serdev_device_put(serdev); + return NOTIFY_DONE; + } + } + break; + } + + return NOTIFY_DONE; +} + +static bool pwrseq_pcie_m2_check_remote_node(struct device *dev, u8 port, const char *node) +{ + struct device_node *remote; + + remote = of_graph_get_remote_node(dev_of_node(dev), port, -1); + if (remote && of_node_name_eq(remote, node)) { + of_node_put(remote); + return true; + } + + of_node_put(remote); + + return false; +} + +/* + * If the connector exposes a non-discoverable bus like UART, the respective + * protocol device needs to be created manually with the help of the notifier + * of the discoverable bus like PCIe. + */ +static void pwrseq_pcie_m2_register_notifier(struct pwrseq_pcie_m2_ctx *ctx, struct device *dev) +{ + int ret; + + /* + * Register a PCI notifier for Key E connector that has PCIe as Port 0 + * interface and Serial as Port 1 interface. + */ + if (pwrseq_pcie_m2_check_remote_node(dev, 1, "serial")) { + if (pwrseq_pcie_m2_check_remote_node(dev, 0, "pcie")) { + ctx->dev = dev; + ctx->nb.notifier_call = pwrseq_m2_pcie_notify; + ret = (bus_register_notifier(&pci_bus_type, &ctx->nb)); + if (ret) { + dev_err_probe(dev, ret, "Failed to register notifier for serdev\n"); + return; + } + } + } +} + static int pwrseq_pcie_m2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -122,6 +308,16 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to get all regulators\n"); + ctx->w_disable1_gpio = devm_gpiod_get_optional(dev, "w_disable1", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->w_disable1_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->w_disable1_gpio), + "Failed to get the W_DISABLE_1# GPIO\n"); + + ctx->w_disable2_gpio = devm_gpiod_get_optional(dev, "w_disable2", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->w_disable2_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->w_disable2_gpio), + "Failed to get the W_DISABLE_2# GPIO\n"); + ctx->num_vregs = ret; config.parent = dev; @@ -137,6 +333,12 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev) "Failed to register the power sequencer\n"); } + /* + * Register an optional notifier for creating protocol devices for + * non-discoverable busses like UART. + */ + pwrseq_pcie_m2_register_notifier(ctx, dev); + return 0; } @@ -145,6 +347,10 @@ static const struct of_device_id pwrseq_pcie_m2_of_match[] = { .compatible = "pcie-m2-m-connector", .data = &pwrseq_pcie_m2_m_of_data, }, + { + .compatible = "pcie-m2-e-connector", + .data = &pwrseq_pcie_m2_e_of_data, + }, { } }; MODULE_DEVICE_TABLE(of, pwrseq_pcie_m2_of_match); -- 2.48.1