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