xref: /linux/drivers/power/sequencing/pwrseq-pcie-m2.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
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");
169