Skip to content

Commit 52e7b5b

Browse files
Mani-SadhasivamBartosz Golaszewski
authored andcommitted
power: sequencing: Add the Power Sequencing driver for the PCIe M.2 connectors
This driver is used to control the PCIe M.2 connectors of different Mechanical Keys attached to the host machines and supporting different interfaces like PCIe/SATA, USB/UART etc... Currently, this driver supports only the Mechanical Key M connectors with PCIe interface. The driver also only supports driving the mandatory 3.3v and optional 1.8v power supplies. The optional signals of the Key M connectors are not currently supported. Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> Link: https://lore.kernel.org/r/20260122-pci-m2-v6-4-575da9f97239@oss.qualcomm.com [Bartosz: rename pwrseq_pcie_m2_free_resources() to pwrseq_pcie_m2_free_regulators()] Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent 926194a commit 52e7b5b

4 files changed

Lines changed: 184 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20791,6 +20791,13 @@ F: Documentation/driver-api/pwrseq.rst
2079120791
F: drivers/power/sequencing/
2079220792
F: include/linux/pwrseq/
2079320793

20794+
PCIE M.2 POWER SEQUENCING
20795+
M: Manivannan Sadhasivam <mani@kernel.org>
20796+
L: linux-pci@vger.kernel.org
20797+
S: Maintained
20798+
F: Documentation/devicetree/bindings/connector/pcie-m2-m-connector.yaml
20799+
F: drivers/power/sequencing/pwrseq-pcie-m2.c
20800+
2079420801
POWER STATE COORDINATION INTERFACE (PSCI)
2079520802
M: Mark Rutland <mark.rutland@arm.com>
2079620803
M: Lorenzo Pieralisi <lpieralisi@kernel.org>

drivers/power/sequencing/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,12 @@ config POWER_SEQUENCING_TH1520_GPU
3535
GPU. This driver handles the complex clock and reset sequence
3636
required to power on the Imagination BXM GPU on this platform.
3737

38+
config POWER_SEQUENCING_PCIE_M2
39+
tristate "PCIe M.2 connector power sequencing driver"
40+
depends on OF || COMPILE_TEST
41+
help
42+
Say Y here to enable the power sequencing driver for PCIe M.2
43+
connectors. This driver handles the power sequencing for the M.2
44+
connectors exposing multiple interfaces like PCIe, SATA, UART, etc...
45+
3846
endif

drivers/power/sequencing/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pwrseq-core-y := core.o
55

66
obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o
77
obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) += pwrseq-thead-gpu.o
8+
obj-$(CONFIG_POWER_SEQUENCING_PCIE_M2) += pwrseq-pcie-m2.o
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4+
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
5+
*/
6+
7+
#include <linux/device.h>
8+
#include <linux/mod_devicetable.h>
9+
#include <linux/module.h>
10+
#include <linux/of.h>
11+
#include <linux/of_graph.h>
12+
#include <linux/platform_device.h>
13+
#include <linux/pwrseq/provider.h>
14+
#include <linux/regulator/consumer.h>
15+
#include <linux/slab.h>
16+
17+
struct pwrseq_pcie_m2_pdata {
18+
const struct pwrseq_target_data **targets;
19+
};
20+
21+
struct pwrseq_pcie_m2_ctx {
22+
struct pwrseq_device *pwrseq;
23+
struct device_node *of_node;
24+
const struct pwrseq_pcie_m2_pdata *pdata;
25+
struct regulator_bulk_data *regs;
26+
size_t num_vregs;
27+
struct notifier_block nb;
28+
};
29+
30+
static int pwrseq_pcie_m2_m_vregs_enable(struct pwrseq_device *pwrseq)
31+
{
32+
struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
33+
34+
return regulator_bulk_enable(ctx->num_vregs, ctx->regs);
35+
}
36+
37+
static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq)
38+
{
39+
struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
40+
41+
return regulator_bulk_disable(ctx->num_vregs, ctx->regs);
42+
}
43+
44+
static const struct pwrseq_unit_data pwrseq_pcie_m2_vregs_unit_data = {
45+
.name = "regulators-enable",
46+
.enable = pwrseq_pcie_m2_m_vregs_enable,
47+
.disable = pwrseq_pcie_m2_m_vregs_disable,
48+
};
49+
50+
static const struct pwrseq_unit_data *pwrseq_pcie_m2_m_unit_deps[] = {
51+
&pwrseq_pcie_m2_vregs_unit_data,
52+
NULL
53+
};
54+
55+
static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = {
56+
.name = "pcie-enable",
57+
.deps = pwrseq_pcie_m2_m_unit_deps,
58+
};
59+
60+
static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = {
61+
.name = "pcie",
62+
.unit = &pwrseq_pcie_m2_m_pcie_unit_data,
63+
};
64+
65+
static const struct pwrseq_target_data *pwrseq_pcie_m2_m_targets[] = {
66+
&pwrseq_pcie_m2_m_pcie_target_data,
67+
NULL
68+
};
69+
70+
static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_m_of_data = {
71+
.targets = pwrseq_pcie_m2_m_targets,
72+
};
73+
74+
static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
75+
struct device *dev)
76+
{
77+
struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
78+
struct device_node *endpoint __free(device_node) = NULL;
79+
80+
/*
81+
* Traverse the 'remote-endpoint' nodes and check if the remote node's
82+
* parent matches the OF node of 'dev'.
83+
*/
84+
for_each_endpoint_of_node(ctx->of_node, endpoint) {
85+
struct device_node *remote __free(device_node) =
86+
of_graph_get_remote_port_parent(endpoint);
87+
if (remote && (remote == dev_of_node(dev)))
88+
return PWRSEQ_MATCH_OK;
89+
}
90+
91+
return PWRSEQ_NO_MATCH;
92+
}
93+
94+
static void pwrseq_pcie_m2_free_regulators(void *data)
95+
{
96+
struct pwrseq_pcie_m2_ctx *ctx = data;
97+
98+
regulator_bulk_free(ctx->num_vregs, ctx->regs);
99+
}
100+
101+
static int pwrseq_pcie_m2_probe(struct platform_device *pdev)
102+
{
103+
struct device *dev = &pdev->dev;
104+
struct pwrseq_pcie_m2_ctx *ctx;
105+
struct pwrseq_config config = {};
106+
int ret;
107+
108+
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
109+
if (!ctx)
110+
return -ENOMEM;
111+
112+
ctx->of_node = of_node_get(dev->of_node);
113+
ctx->pdata = device_get_match_data(dev);
114+
if (!ctx->pdata)
115+
return dev_err_probe(dev, -ENODEV,
116+
"Failed to obtain platform data\n");
117+
118+
/*
119+
* Currently, of_regulator_bulk_get_all() is the only regulator API that
120+
* allows to get all supplies in the devicetree node without manually
121+
* specifying them.
122+
*/
123+
ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &ctx->regs);
124+
if (ret < 0)
125+
return dev_err_probe(dev, ret,
126+
"Failed to get all regulators\n");
127+
128+
ctx->num_vregs = ret;
129+
130+
ret = devm_add_action_or_reset(dev, pwrseq_pcie_m2_free_regulators, ctx);
131+
if (ret)
132+
return ret;
133+
134+
config.parent = dev;
135+
config.owner = THIS_MODULE;
136+
config.drvdata = ctx;
137+
config.match = pwrseq_pcie_m2_match;
138+
config.targets = ctx->pdata->targets;
139+
140+
ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
141+
if (IS_ERR(ctx->pwrseq))
142+
return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),
143+
"Failed to register the power sequencer\n");
144+
145+
return 0;
146+
}
147+
148+
static const struct of_device_id pwrseq_pcie_m2_of_match[] = {
149+
{
150+
.compatible = "pcie-m2-m-connector",
151+
.data = &pwrseq_pcie_m2_m_of_data,
152+
},
153+
{ }
154+
};
155+
MODULE_DEVICE_TABLE(of, pwrseq_pcie_m2_of_match);
156+
157+
static struct platform_driver pwrseq_pcie_m2_driver = {
158+
.driver = {
159+
.name = "pwrseq-pcie-m2",
160+
.of_match_table = pwrseq_pcie_m2_of_match,
161+
},
162+
.probe = pwrseq_pcie_m2_probe,
163+
};
164+
module_platform_driver(pwrseq_pcie_m2_driver);
165+
166+
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>");
167+
MODULE_DESCRIPTION("Power Sequencing driver for PCIe M.2 connector");
168+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)