From: Manivannan Sadhasivam via B4 Relay <devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org>
To: "Rob Herring" <robh@kernel.org>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Jiri Slaby" <jirislaby@kernel.org>,
"Nathan Chancellor" <nathan@kernel.org>,
"Nicolas Schier" <nicolas.schier@linux.dev>,
"Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
"Mark Pearson" <mpearson-lenovo@squebb.ca>,
"Derek J. Clark" <derekjohn.clark@gmail.com>,
"Manivannan Sadhasivam" <mani@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Marcel Holtmann" <marcel@holtmann.org>,
"Luiz Augusto von Dentz" <luiz.dentz@gmail.com>,
"Bartosz Golaszewski" <brgl@bgdev.pl>
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 <stephan.gerhold@linaro.org>,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Subject: [PATCH 9/9] power: sequencing: pcie-m2: Add support for PCIe M.2 Key E connectors
Date: Wed, 12 Nov 2025 20:15:21 +0530 [thread overview]
Message-ID: <20251112-pci-m2-e-v1-9-97413d6bf824@oss.qualcomm.com> (raw)
In-Reply-To: <20251112-pci-m2-e-v1-0-97413d6bf824@oss.qualcomm.com>
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
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 <manivannan.sadhasivam@oss.qualcomm.com>
---
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 <linux/device.h>
+#include <linux/gpio/consumer.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#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 {
@@ -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
next prev parent reply other threads:[~2025-11-12 14:45 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-12 14:45 [PATCH 0/9] Add support for handling PCIe M.2 Key E connectors in devicetree Manivannan Sadhasivam via B4 Relay
2025-11-12 14:45 ` [PATCH 1/9] serdev: Convert to_serdev_device() and to_serdev_controller() helpers to macros Manivannan Sadhasivam via B4 Relay
2025-11-12 21:13 ` Rob Herring
2025-11-13 5:15 ` Manivannan Sadhasivam
2025-11-12 14:45 ` [PATCH 2/9] serdev: Add serdev device based driver match support Manivannan Sadhasivam via B4 Relay
2025-11-12 14:45 ` [PATCH 3/9] serdev: Allow passing the serdev device name to serdev_device_add() Manivannan Sadhasivam via B4 Relay
2025-11-18 9:41 ` Ilpo Järvinen
2025-11-12 14:45 ` [PATCH 4/9] serdev: Add an API to find the serdev controller associated with the devicetree node Manivannan Sadhasivam via B4 Relay
2025-11-12 14:45 ` [PATCH 5/9] serdev: Add modalias support for serdev client devices Manivannan Sadhasivam via B4 Relay
2025-11-12 14:45 ` [PATCH 6/9] serdev: Skip registering serdev devices from DT is external connector is used Manivannan Sadhasivam via B4 Relay
2025-11-18 13:03 ` Rob Herring
2025-11-19 13:32 ` Manivannan Sadhasivam
2025-11-20 16:22 ` Rob Herring
2025-11-20 16:54 ` Manivannan Sadhasivam
2025-11-12 14:45 ` [PATCH 7/9] dt-bindings: connector: Add PCIe M.2 Mechanical Key E connector Manivannan Sadhasivam via B4 Relay
2025-11-12 17:11 ` Frank Li
2025-11-13 5:00 ` Manivannan Sadhasivam
2025-11-19 23:59 ` Rob Herring
2025-11-20 12:57 ` Manivannan Sadhasivam
2025-11-12 20:08 ` Dmitry Baryshkov
2025-11-13 5:05 ` Manivannan Sadhasivam
2025-11-12 14:45 ` [PATCH 8/9] Bluetooth: hci_qca: Add support for WCN7850 PCIe M.2 card Manivannan Sadhasivam via B4 Relay
2025-11-18 14:29 ` Bartosz Golaszewski
2025-11-19 13:36 ` Manivannan Sadhasivam
2025-11-12 14:45 ` Manivannan Sadhasivam via B4 Relay [this message]
2025-11-19 13:28 ` [PATCH 9/9] power: sequencing: pcie-m2: Add support for PCIe M.2 Key E connectors Bartosz Golaszewski
2025-11-19 13:54 ` Manivannan Sadhasivam
2025-11-12 21:07 ` [PATCH 0/9] Add support for handling PCIe M.2 Key E connectors in devicetree Rob Herring
2025-11-13 5:13 ` Manivannan Sadhasivam
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251112-pci-m2-e-v1-9-97413d6bf824@oss.qualcomm.com \
--to=devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org \
--cc=brgl@bgdev.pl \
--cc=conor+dt@kernel.org \
--cc=derekjohn.clark@gmail.com \
--cc=devicetree@vger.kernel.org \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=gregkh@linuxfoundation.org \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=jirislaby@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-bluetooth@vger.kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=luiz.dentz@gmail.com \
--cc=mani@kernel.org \
--cc=manivannan.sadhasivam@oss.qualcomm.com \
--cc=marcel@holtmann.org \
--cc=mpearson-lenovo@squebb.ca \
--cc=nathan@kernel.org \
--cc=nicolas.schier@linux.dev \
--cc=platform-driver-x86@vger.kernel.org \
--cc=robh@kernel.org \
--cc=stephan.gerhold@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).