xref: /linux/drivers/power/sequencing/core.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1249ebf3fSBartosz Golaszewski // SPDX-License-Identifier: GPL-2.0-only
2249ebf3fSBartosz Golaszewski /*
3249ebf3fSBartosz Golaszewski  * Copyright (C) 2024 Linaro Ltd.
4249ebf3fSBartosz Golaszewski  */
5249ebf3fSBartosz Golaszewski 
6249ebf3fSBartosz Golaszewski #include <linux/bug.h>
7249ebf3fSBartosz Golaszewski #include <linux/cleanup.h>
8249ebf3fSBartosz Golaszewski #include <linux/debugfs.h>
9249ebf3fSBartosz Golaszewski #include <linux/device.h>
10249ebf3fSBartosz Golaszewski #include <linux/err.h>
11249ebf3fSBartosz Golaszewski #include <linux/export.h>
12249ebf3fSBartosz Golaszewski #include <linux/idr.h>
13249ebf3fSBartosz Golaszewski #include <linux/kernel.h>
14249ebf3fSBartosz Golaszewski #include <linux/kref.h>
15249ebf3fSBartosz Golaszewski #include <linux/list.h>
16249ebf3fSBartosz Golaszewski #include <linux/lockdep.h>
17249ebf3fSBartosz Golaszewski #include <linux/module.h>
18249ebf3fSBartosz Golaszewski #include <linux/mutex.h>
19249ebf3fSBartosz Golaszewski #include <linux/property.h>
20249ebf3fSBartosz Golaszewski #include <linux/pwrseq/consumer.h>
21249ebf3fSBartosz Golaszewski #include <linux/pwrseq/provider.h>
22249ebf3fSBartosz Golaszewski #include <linux/radix-tree.h>
23249ebf3fSBartosz Golaszewski #include <linux/rwsem.h>
24249ebf3fSBartosz Golaszewski #include <linux/slab.h>
25249ebf3fSBartosz Golaszewski 
26249ebf3fSBartosz Golaszewski /*
27249ebf3fSBartosz Golaszewski  * Power-sequencing framework for linux.
28249ebf3fSBartosz Golaszewski  *
29249ebf3fSBartosz Golaszewski  * This subsystem allows power sequence providers to register a set of targets
30249ebf3fSBartosz Golaszewski  * that consumers may request and power-up/down.
31249ebf3fSBartosz Golaszewski  *
32249ebf3fSBartosz Golaszewski  * Glossary:
33249ebf3fSBartosz Golaszewski  *
34249ebf3fSBartosz Golaszewski  * Unit - a unit is a discreet chunk of a power sequence. For instance one unit
35249ebf3fSBartosz Golaszewski  * may enable a set of regulators, another may enable a specific GPIO. Units
36249ebf3fSBartosz Golaszewski  * can define dependencies in the form of other units that must be enabled
37249ebf3fSBartosz Golaszewski  * before it itself can be.
38249ebf3fSBartosz Golaszewski  *
39249ebf3fSBartosz Golaszewski  * Target - a target is a set of units (composed of the "final" unit and its
40249ebf3fSBartosz Golaszewski  * dependencies) that a consumer selects by its name when requesting a handle
41249ebf3fSBartosz Golaszewski  * to the power sequencer. Via the dependency system, multiple targets may
42249ebf3fSBartosz Golaszewski  * share the same parts of a power sequence but ignore parts that are
43249ebf3fSBartosz Golaszewski  * irrelevant.
44249ebf3fSBartosz Golaszewski  *
45249ebf3fSBartosz Golaszewski  * Descriptor - a handle passed by the pwrseq core to every consumer that
46249ebf3fSBartosz Golaszewski  * serves as the entry point to the provider layer. It ensures coherence
47249ebf3fSBartosz Golaszewski  * between different users and keeps reference counting consistent.
48249ebf3fSBartosz Golaszewski  *
49249ebf3fSBartosz Golaszewski  * Each provider must define a .match() callback whose role is to determine
50249ebf3fSBartosz Golaszewski  * whether a potential consumer is in fact associated with this sequencer.
51249ebf3fSBartosz Golaszewski  * This allows creating abstraction layers on top of regular device-tree
52249ebf3fSBartosz Golaszewski  * resources like regulators, clocks and other nodes connected to the consumer
53249ebf3fSBartosz Golaszewski  * via phandle.
54249ebf3fSBartosz Golaszewski  */
55249ebf3fSBartosz Golaszewski 
56249ebf3fSBartosz Golaszewski static DEFINE_IDA(pwrseq_ida);
57249ebf3fSBartosz Golaszewski 
58249ebf3fSBartosz Golaszewski /*
59249ebf3fSBartosz Golaszewski  * Protects the device list on the pwrseq bus from concurrent modifications
60249ebf3fSBartosz Golaszewski  * but allows simultaneous read-only access.
61249ebf3fSBartosz Golaszewski  */
62249ebf3fSBartosz Golaszewski static DECLARE_RWSEM(pwrseq_sem);
63249ebf3fSBartosz Golaszewski 
64249ebf3fSBartosz Golaszewski /**
65249ebf3fSBartosz Golaszewski  * struct pwrseq_unit - Private power-sequence unit data.
66249ebf3fSBartosz Golaszewski  * @ref: Reference count for this object. When it goes to 0, the object is
67249ebf3fSBartosz Golaszewski  *       destroyed.
68249ebf3fSBartosz Golaszewski  * @name: Name of this target.
69249ebf3fSBartosz Golaszewski  * @list: Link to siblings on the list of all units of a single sequencer.
70249ebf3fSBartosz Golaszewski  * @deps: List of units on which this unit depends.
71249ebf3fSBartosz Golaszewski  * @enable: Callback running the part of the power-on sequence provided by
72249ebf3fSBartosz Golaszewski  *          this unit.
73249ebf3fSBartosz Golaszewski  * @disable: Callback running the part of the power-off sequence provided
74249ebf3fSBartosz Golaszewski  *           by this unit.
75249ebf3fSBartosz Golaszewski  * @enable_count: Current number of users that enabled this unit. May be the
76249ebf3fSBartosz Golaszewski  *                consumer of the power sequencer or other units that depend
77249ebf3fSBartosz Golaszewski  *                on this one.
78249ebf3fSBartosz Golaszewski  */
79249ebf3fSBartosz Golaszewski struct pwrseq_unit {
80249ebf3fSBartosz Golaszewski 	struct kref ref;
81249ebf3fSBartosz Golaszewski 	const char *name;
82249ebf3fSBartosz Golaszewski 	struct list_head list;
83249ebf3fSBartosz Golaszewski 	struct list_head deps;
84249ebf3fSBartosz Golaszewski 	pwrseq_power_state_func enable;
85249ebf3fSBartosz Golaszewski 	pwrseq_power_state_func disable;
86249ebf3fSBartosz Golaszewski 	unsigned int enable_count;
87249ebf3fSBartosz Golaszewski };
88249ebf3fSBartosz Golaszewski 
pwrseq_unit_new(const struct pwrseq_unit_data * data)89249ebf3fSBartosz Golaszewski static struct pwrseq_unit *pwrseq_unit_new(const struct pwrseq_unit_data *data)
90249ebf3fSBartosz Golaszewski {
91249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
92249ebf3fSBartosz Golaszewski 
93249ebf3fSBartosz Golaszewski 	unit = kzalloc(sizeof(*unit), GFP_KERNEL);
94249ebf3fSBartosz Golaszewski 	if (!unit)
95249ebf3fSBartosz Golaszewski 		return NULL;
96249ebf3fSBartosz Golaszewski 
97249ebf3fSBartosz Golaszewski 	unit->name = kstrdup_const(data->name, GFP_KERNEL);
98249ebf3fSBartosz Golaszewski 	if (!unit->name) {
99249ebf3fSBartosz Golaszewski 		kfree(unit);
100249ebf3fSBartosz Golaszewski 		return NULL;
101249ebf3fSBartosz Golaszewski 	}
102249ebf3fSBartosz Golaszewski 
103249ebf3fSBartosz Golaszewski 	kref_init(&unit->ref);
104249ebf3fSBartosz Golaszewski 	INIT_LIST_HEAD(&unit->deps);
105249ebf3fSBartosz Golaszewski 	unit->enable = data->enable;
106249ebf3fSBartosz Golaszewski 	unit->disable = data->disable;
107249ebf3fSBartosz Golaszewski 
108249ebf3fSBartosz Golaszewski 	return unit;
109249ebf3fSBartosz Golaszewski }
110249ebf3fSBartosz Golaszewski 
pwrseq_unit_get(struct pwrseq_unit * unit)111249ebf3fSBartosz Golaszewski static struct pwrseq_unit *pwrseq_unit_get(struct pwrseq_unit *unit)
112249ebf3fSBartosz Golaszewski {
113249ebf3fSBartosz Golaszewski 	kref_get(&unit->ref);
114249ebf3fSBartosz Golaszewski 
115249ebf3fSBartosz Golaszewski 	return unit;
116249ebf3fSBartosz Golaszewski }
117249ebf3fSBartosz Golaszewski 
118249ebf3fSBartosz Golaszewski static void pwrseq_unit_release(struct kref *ref);
119249ebf3fSBartosz Golaszewski 
pwrseq_unit_put(struct pwrseq_unit * unit)120249ebf3fSBartosz Golaszewski static void pwrseq_unit_put(struct pwrseq_unit *unit)
121249ebf3fSBartosz Golaszewski {
122249ebf3fSBartosz Golaszewski 	kref_put(&unit->ref, pwrseq_unit_release);
123249ebf3fSBartosz Golaszewski }
124249ebf3fSBartosz Golaszewski 
125249ebf3fSBartosz Golaszewski /**
126249ebf3fSBartosz Golaszewski  * struct pwrseq_unit_dep - Wrapper around a reference to the unit structure
127249ebf3fSBartosz Golaszewski  *                          allowing to keep it on multiple dependency lists
128249ebf3fSBartosz Golaszewski  *                          in different units.
129249ebf3fSBartosz Golaszewski  * @list: Siblings on the list.
130249ebf3fSBartosz Golaszewski  * @unit: Address of the referenced unit.
131249ebf3fSBartosz Golaszewski  */
132249ebf3fSBartosz Golaszewski struct pwrseq_unit_dep {
133249ebf3fSBartosz Golaszewski 	struct list_head list;
134249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
135249ebf3fSBartosz Golaszewski };
136249ebf3fSBartosz Golaszewski 
pwrseq_unit_dep_new(struct pwrseq_unit * unit)137249ebf3fSBartosz Golaszewski static struct pwrseq_unit_dep *pwrseq_unit_dep_new(struct pwrseq_unit *unit)
138249ebf3fSBartosz Golaszewski {
139249ebf3fSBartosz Golaszewski 	struct pwrseq_unit_dep *dep;
140249ebf3fSBartosz Golaszewski 
141249ebf3fSBartosz Golaszewski 	dep = kzalloc(sizeof(*dep), GFP_KERNEL);
142249ebf3fSBartosz Golaszewski 	if (!dep)
143249ebf3fSBartosz Golaszewski 		return NULL;
144249ebf3fSBartosz Golaszewski 
145249ebf3fSBartosz Golaszewski 	dep->unit = unit;
146249ebf3fSBartosz Golaszewski 
147249ebf3fSBartosz Golaszewski 	return dep;
148249ebf3fSBartosz Golaszewski }
149249ebf3fSBartosz Golaszewski 
pwrseq_unit_dep_free(struct pwrseq_unit_dep * ref)150249ebf3fSBartosz Golaszewski static void pwrseq_unit_dep_free(struct pwrseq_unit_dep *ref)
151249ebf3fSBartosz Golaszewski {
152249ebf3fSBartosz Golaszewski 	pwrseq_unit_put(ref->unit);
153249ebf3fSBartosz Golaszewski 	kfree(ref);
154249ebf3fSBartosz Golaszewski }
155249ebf3fSBartosz Golaszewski 
pwrseq_unit_free_deps(struct list_head * list)156249ebf3fSBartosz Golaszewski static void pwrseq_unit_free_deps(struct list_head *list)
157249ebf3fSBartosz Golaszewski {
158249ebf3fSBartosz Golaszewski 	struct pwrseq_unit_dep *dep, *next;
159249ebf3fSBartosz Golaszewski 
160249ebf3fSBartosz Golaszewski 	list_for_each_entry_safe(dep, next, list, list) {
161249ebf3fSBartosz Golaszewski 		list_del(&dep->list);
162249ebf3fSBartosz Golaszewski 		pwrseq_unit_dep_free(dep);
163249ebf3fSBartosz Golaszewski 	}
164249ebf3fSBartosz Golaszewski }
165249ebf3fSBartosz Golaszewski 
pwrseq_unit_release(struct kref * ref)166249ebf3fSBartosz Golaszewski static void pwrseq_unit_release(struct kref *ref)
167249ebf3fSBartosz Golaszewski {
168249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit = container_of(ref, struct pwrseq_unit, ref);
169249ebf3fSBartosz Golaszewski 
170249ebf3fSBartosz Golaszewski 	pwrseq_unit_free_deps(&unit->deps);
171249ebf3fSBartosz Golaszewski 	list_del(&unit->list);
172249ebf3fSBartosz Golaszewski 	kfree_const(unit->name);
173249ebf3fSBartosz Golaszewski 	kfree(unit);
174249ebf3fSBartosz Golaszewski }
175249ebf3fSBartosz Golaszewski 
176249ebf3fSBartosz Golaszewski /**
177249ebf3fSBartosz Golaszewski  * struct pwrseq_target - Private power-sequence target data.
178249ebf3fSBartosz Golaszewski  * @list: Siblings on the list of all targets exposed by a power sequencer.
179249ebf3fSBartosz Golaszewski  * @name: Name of the target.
180249ebf3fSBartosz Golaszewski  * @unit: Final unit for this target.
181249ebf3fSBartosz Golaszewski  * @post_enable: Callback run after the target unit has been enabled, *after*
182249ebf3fSBartosz Golaszewski  *               the state lock has been released. It's useful for implementing
183249ebf3fSBartosz Golaszewski  *               boot-up delays without blocking other users from powering up
184249ebf3fSBartosz Golaszewski  *               using the same power sequencer.
185249ebf3fSBartosz Golaszewski  */
186249ebf3fSBartosz Golaszewski struct pwrseq_target {
187249ebf3fSBartosz Golaszewski 	struct list_head list;
188249ebf3fSBartosz Golaszewski 	const char *name;
189249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
190249ebf3fSBartosz Golaszewski 	pwrseq_power_state_func post_enable;
191249ebf3fSBartosz Golaszewski };
192249ebf3fSBartosz Golaszewski 
193249ebf3fSBartosz Golaszewski static struct pwrseq_target *
pwrseq_target_new(const struct pwrseq_target_data * data)194249ebf3fSBartosz Golaszewski pwrseq_target_new(const struct pwrseq_target_data *data)
195249ebf3fSBartosz Golaszewski {
196249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
197249ebf3fSBartosz Golaszewski 
198249ebf3fSBartosz Golaszewski 	target = kzalloc(sizeof(*target), GFP_KERNEL);
199249ebf3fSBartosz Golaszewski 	if (!target)
200249ebf3fSBartosz Golaszewski 		return NULL;
201249ebf3fSBartosz Golaszewski 
202249ebf3fSBartosz Golaszewski 	target->name = kstrdup_const(data->name, GFP_KERNEL);
203249ebf3fSBartosz Golaszewski 	if (!target->name) {
204249ebf3fSBartosz Golaszewski 		kfree(target);
205249ebf3fSBartosz Golaszewski 		return NULL;
206249ebf3fSBartosz Golaszewski 	}
207249ebf3fSBartosz Golaszewski 
208249ebf3fSBartosz Golaszewski 	target->post_enable = data->post_enable;
209249ebf3fSBartosz Golaszewski 
210249ebf3fSBartosz Golaszewski 	return target;
211249ebf3fSBartosz Golaszewski }
212249ebf3fSBartosz Golaszewski 
pwrseq_target_free(struct pwrseq_target * target)213249ebf3fSBartosz Golaszewski static void pwrseq_target_free(struct pwrseq_target *target)
214249ebf3fSBartosz Golaszewski {
215*a19ce320SBartosz Golaszewski 	if (!IS_ERR_OR_NULL(target->unit))
216249ebf3fSBartosz Golaszewski 		pwrseq_unit_put(target->unit);
217249ebf3fSBartosz Golaszewski 	kfree_const(target->name);
218249ebf3fSBartosz Golaszewski 	kfree(target);
219249ebf3fSBartosz Golaszewski }
220249ebf3fSBartosz Golaszewski 
221249ebf3fSBartosz Golaszewski /**
222249ebf3fSBartosz Golaszewski  * struct pwrseq_device - Private power sequencing data.
223249ebf3fSBartosz Golaszewski  * @dev: Device struct associated with this sequencer.
224249ebf3fSBartosz Golaszewski  * @id: Device ID.
225249ebf3fSBartosz Golaszewski  * @owner: Prevents removal of active power sequencing providers.
226249ebf3fSBartosz Golaszewski  * @rw_lock: Protects the device from being unregistered while in use.
227249ebf3fSBartosz Golaszewski  * @state_lock: Prevents multiple users running the power sequence at the same
228249ebf3fSBartosz Golaszewski  *              time.
229249ebf3fSBartosz Golaszewski  * @match: Power sequencer matching callback.
230249ebf3fSBartosz Golaszewski  * @targets: List of targets exposed by this sequencer.
231249ebf3fSBartosz Golaszewski  * @units: List of all units supported by this sequencer.
232249ebf3fSBartosz Golaszewski  */
233249ebf3fSBartosz Golaszewski struct pwrseq_device {
234249ebf3fSBartosz Golaszewski 	struct device dev;
235249ebf3fSBartosz Golaszewski 	int id;
236249ebf3fSBartosz Golaszewski 	struct module *owner;
237249ebf3fSBartosz Golaszewski 	struct rw_semaphore rw_lock;
238249ebf3fSBartosz Golaszewski 	struct mutex state_lock;
239249ebf3fSBartosz Golaszewski 	pwrseq_match_func match;
240249ebf3fSBartosz Golaszewski 	struct list_head targets;
241249ebf3fSBartosz Golaszewski 	struct list_head units;
242249ebf3fSBartosz Golaszewski };
243249ebf3fSBartosz Golaszewski 
to_pwrseq_device(struct device * dev)244249ebf3fSBartosz Golaszewski static struct pwrseq_device *to_pwrseq_device(struct device *dev)
245249ebf3fSBartosz Golaszewski {
246249ebf3fSBartosz Golaszewski 	return container_of(dev, struct pwrseq_device, dev);
247249ebf3fSBartosz Golaszewski }
248249ebf3fSBartosz Golaszewski 
pwrseq_device_get(struct pwrseq_device * pwrseq)249249ebf3fSBartosz Golaszewski static struct pwrseq_device *pwrseq_device_get(struct pwrseq_device *pwrseq)
250249ebf3fSBartosz Golaszewski {
251249ebf3fSBartosz Golaszewski 	get_device(&pwrseq->dev);
252249ebf3fSBartosz Golaszewski 
253249ebf3fSBartosz Golaszewski 	return pwrseq;
254249ebf3fSBartosz Golaszewski }
255249ebf3fSBartosz Golaszewski 
pwrseq_device_put(struct pwrseq_device * pwrseq)256249ebf3fSBartosz Golaszewski static void pwrseq_device_put(struct pwrseq_device *pwrseq)
257249ebf3fSBartosz Golaszewski {
258249ebf3fSBartosz Golaszewski 	put_device(&pwrseq->dev);
259249ebf3fSBartosz Golaszewski }
260249ebf3fSBartosz Golaszewski 
261249ebf3fSBartosz Golaszewski /**
262249ebf3fSBartosz Golaszewski  * struct pwrseq_desc - Wraps access to the pwrseq_device and ensures that one
263249ebf3fSBartosz Golaszewski  *                      user cannot break the reference counting for others.
264249ebf3fSBartosz Golaszewski  * @pwrseq: Reference to the power sequencing device.
265249ebf3fSBartosz Golaszewski  * @target: Reference to the target this descriptor allows to control.
266249ebf3fSBartosz Golaszewski  * @powered_on: Power state set by the holder of the descriptor (not necessarily
267249ebf3fSBartosz Golaszewski  * corresponding to the actual power state of the device).
268249ebf3fSBartosz Golaszewski  */
269249ebf3fSBartosz Golaszewski struct pwrseq_desc {
270249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq;
271249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
272249ebf3fSBartosz Golaszewski 	bool powered_on;
273249ebf3fSBartosz Golaszewski };
274249ebf3fSBartosz Golaszewski 
275249ebf3fSBartosz Golaszewski static const struct bus_type pwrseq_bus = {
276249ebf3fSBartosz Golaszewski 	.name = "pwrseq",
277249ebf3fSBartosz Golaszewski };
278249ebf3fSBartosz Golaszewski 
pwrseq_release(struct device * dev)279249ebf3fSBartosz Golaszewski static void pwrseq_release(struct device *dev)
280249ebf3fSBartosz Golaszewski {
281249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq = to_pwrseq_device(dev);
282249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target, *pos;
283249ebf3fSBartosz Golaszewski 
284249ebf3fSBartosz Golaszewski 	list_for_each_entry_safe(target, pos, &pwrseq->targets, list) {
285249ebf3fSBartosz Golaszewski 		list_del(&target->list);
286249ebf3fSBartosz Golaszewski 		pwrseq_target_free(target);
287249ebf3fSBartosz Golaszewski 	}
288249ebf3fSBartosz Golaszewski 
289249ebf3fSBartosz Golaszewski 	mutex_destroy(&pwrseq->state_lock);
290249ebf3fSBartosz Golaszewski 	ida_free(&pwrseq_ida, pwrseq->id);
291249ebf3fSBartosz Golaszewski 	kfree(pwrseq);
292249ebf3fSBartosz Golaszewski }
293249ebf3fSBartosz Golaszewski 
294249ebf3fSBartosz Golaszewski static const struct device_type pwrseq_device_type = {
295249ebf3fSBartosz Golaszewski 	.name = "power_sequencer",
296249ebf3fSBartosz Golaszewski 	.release = pwrseq_release,
297249ebf3fSBartosz Golaszewski };
298249ebf3fSBartosz Golaszewski 
pwrseq_check_unit_deps(const struct pwrseq_unit_data * data,struct radix_tree_root * visited_units)299249ebf3fSBartosz Golaszewski static int pwrseq_check_unit_deps(const struct pwrseq_unit_data *data,
300249ebf3fSBartosz Golaszewski 				  struct radix_tree_root *visited_units)
301249ebf3fSBartosz Golaszewski {
302249ebf3fSBartosz Golaszewski 	const struct pwrseq_unit_data *tmp, **cur;
303249ebf3fSBartosz Golaszewski 	int ret;
304249ebf3fSBartosz Golaszewski 
305249ebf3fSBartosz Golaszewski 	ret = radix_tree_insert(visited_units, (unsigned long)data,
306249ebf3fSBartosz Golaszewski 				(void *)data);
307249ebf3fSBartosz Golaszewski 	if (ret)
308249ebf3fSBartosz Golaszewski 		return ret;
309249ebf3fSBartosz Golaszewski 
310249ebf3fSBartosz Golaszewski 	for (cur = data->deps; cur && *cur; cur++) {
311249ebf3fSBartosz Golaszewski 		tmp = radix_tree_lookup(visited_units, (unsigned long)*cur);
312249ebf3fSBartosz Golaszewski 		if (tmp) {
313249ebf3fSBartosz Golaszewski 			WARN(1, "Circular dependency in power sequencing flow detected!\n");
314249ebf3fSBartosz Golaszewski 			return -EINVAL;
315249ebf3fSBartosz Golaszewski 		}
316249ebf3fSBartosz Golaszewski 
317249ebf3fSBartosz Golaszewski 		ret = pwrseq_check_unit_deps(*cur, visited_units);
318249ebf3fSBartosz Golaszewski 		if (ret)
319249ebf3fSBartosz Golaszewski 			return ret;
320249ebf3fSBartosz Golaszewski 	}
321249ebf3fSBartosz Golaszewski 
322249ebf3fSBartosz Golaszewski 	return 0;
323249ebf3fSBartosz Golaszewski }
324249ebf3fSBartosz Golaszewski 
pwrseq_check_target_deps(const struct pwrseq_target_data * data)325249ebf3fSBartosz Golaszewski static int pwrseq_check_target_deps(const struct pwrseq_target_data *data)
326249ebf3fSBartosz Golaszewski {
327249ebf3fSBartosz Golaszewski 	struct radix_tree_root visited_units;
328249ebf3fSBartosz Golaszewski 	struct radix_tree_iter iter;
329249ebf3fSBartosz Golaszewski 	void __rcu **slot;
330249ebf3fSBartosz Golaszewski 	int ret;
331249ebf3fSBartosz Golaszewski 
332249ebf3fSBartosz Golaszewski 	if (!data->unit)
333249ebf3fSBartosz Golaszewski 		return -EINVAL;
334249ebf3fSBartosz Golaszewski 
335249ebf3fSBartosz Golaszewski 	INIT_RADIX_TREE(&visited_units, GFP_KERNEL);
336249ebf3fSBartosz Golaszewski 	ret = pwrseq_check_unit_deps(data->unit, &visited_units);
337249ebf3fSBartosz Golaszewski 	radix_tree_for_each_slot(slot, &visited_units, &iter, 0)
338249ebf3fSBartosz Golaszewski 		radix_tree_delete(&visited_units, iter.index);
339249ebf3fSBartosz Golaszewski 
340249ebf3fSBartosz Golaszewski 	return ret;
341249ebf3fSBartosz Golaszewski }
342249ebf3fSBartosz Golaszewski 
343249ebf3fSBartosz Golaszewski static int pwrseq_unit_setup_deps(const struct pwrseq_unit_data **data,
344249ebf3fSBartosz Golaszewski 				  struct list_head *dep_list,
345249ebf3fSBartosz Golaszewski 				  struct list_head *unit_list,
346249ebf3fSBartosz Golaszewski 				  struct radix_tree_root *processed_units);
347249ebf3fSBartosz Golaszewski 
348249ebf3fSBartosz Golaszewski static struct pwrseq_unit *
pwrseq_unit_setup(const struct pwrseq_unit_data * data,struct list_head * unit_list,struct radix_tree_root * processed_units)349249ebf3fSBartosz Golaszewski pwrseq_unit_setup(const struct pwrseq_unit_data *data,
350249ebf3fSBartosz Golaszewski 		  struct list_head *unit_list,
351249ebf3fSBartosz Golaszewski 		  struct radix_tree_root *processed_units)
352249ebf3fSBartosz Golaszewski {
353249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
354249ebf3fSBartosz Golaszewski 	int ret;
355249ebf3fSBartosz Golaszewski 
356249ebf3fSBartosz Golaszewski 	unit = radix_tree_lookup(processed_units, (unsigned long)data);
357249ebf3fSBartosz Golaszewski 	if (unit)
358249ebf3fSBartosz Golaszewski 		return pwrseq_unit_get(unit);
359249ebf3fSBartosz Golaszewski 
360249ebf3fSBartosz Golaszewski 	unit = pwrseq_unit_new(data);
361249ebf3fSBartosz Golaszewski 	if (!unit)
362249ebf3fSBartosz Golaszewski 		return ERR_PTR(-ENOMEM);
363249ebf3fSBartosz Golaszewski 
364249ebf3fSBartosz Golaszewski 	if (data->deps) {
365249ebf3fSBartosz Golaszewski 		ret = pwrseq_unit_setup_deps(data->deps, &unit->deps,
366249ebf3fSBartosz Golaszewski 					     unit_list, processed_units);
367249ebf3fSBartosz Golaszewski 		if (ret) {
368249ebf3fSBartosz Golaszewski 			pwrseq_unit_put(unit);
369249ebf3fSBartosz Golaszewski 			return ERR_PTR(ret);
370249ebf3fSBartosz Golaszewski 		}
371249ebf3fSBartosz Golaszewski 	}
372249ebf3fSBartosz Golaszewski 
373249ebf3fSBartosz Golaszewski 	ret = radix_tree_insert(processed_units, (unsigned long)data, unit);
374249ebf3fSBartosz Golaszewski 	if (ret) {
375249ebf3fSBartosz Golaszewski 		pwrseq_unit_put(unit);
376249ebf3fSBartosz Golaszewski 		return ERR_PTR(ret);
377249ebf3fSBartosz Golaszewski 	}
378249ebf3fSBartosz Golaszewski 
379249ebf3fSBartosz Golaszewski 	list_add_tail(&unit->list, unit_list);
380249ebf3fSBartosz Golaszewski 
381249ebf3fSBartosz Golaszewski 	return unit;
382249ebf3fSBartosz Golaszewski }
383249ebf3fSBartosz Golaszewski 
pwrseq_unit_setup_deps(const struct pwrseq_unit_data ** data,struct list_head * dep_list,struct list_head * unit_list,struct radix_tree_root * processed_units)384249ebf3fSBartosz Golaszewski static int pwrseq_unit_setup_deps(const struct pwrseq_unit_data **data,
385249ebf3fSBartosz Golaszewski 				  struct list_head *dep_list,
386249ebf3fSBartosz Golaszewski 				  struct list_head *unit_list,
387249ebf3fSBartosz Golaszewski 				  struct radix_tree_root *processed_units)
388249ebf3fSBartosz Golaszewski {
389249ebf3fSBartosz Golaszewski 	const struct pwrseq_unit_data *pos;
390249ebf3fSBartosz Golaszewski 	struct pwrseq_unit_dep *dep;
391249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
392249ebf3fSBartosz Golaszewski 	int i;
393249ebf3fSBartosz Golaszewski 
394249ebf3fSBartosz Golaszewski 	for (i = 0; data[i]; i++) {
395249ebf3fSBartosz Golaszewski 		pos = data[i];
396249ebf3fSBartosz Golaszewski 
397249ebf3fSBartosz Golaszewski 		unit = pwrseq_unit_setup(pos, unit_list, processed_units);
398249ebf3fSBartosz Golaszewski 		if (IS_ERR(unit))
399249ebf3fSBartosz Golaszewski 			return PTR_ERR(unit);
400249ebf3fSBartosz Golaszewski 
401249ebf3fSBartosz Golaszewski 		dep = pwrseq_unit_dep_new(unit);
402249ebf3fSBartosz Golaszewski 		if (!dep) {
403249ebf3fSBartosz Golaszewski 			pwrseq_unit_put(unit);
404249ebf3fSBartosz Golaszewski 			return -ENOMEM;
405249ebf3fSBartosz Golaszewski 		}
406249ebf3fSBartosz Golaszewski 
407249ebf3fSBartosz Golaszewski 		list_add_tail(&dep->list, dep_list);
408249ebf3fSBartosz Golaszewski 	}
409249ebf3fSBartosz Golaszewski 
410249ebf3fSBartosz Golaszewski 	return 0;
411249ebf3fSBartosz Golaszewski }
412249ebf3fSBartosz Golaszewski 
pwrseq_do_setup_targets(const struct pwrseq_target_data ** data,struct pwrseq_device * pwrseq,struct radix_tree_root * processed_units)413249ebf3fSBartosz Golaszewski static int pwrseq_do_setup_targets(const struct pwrseq_target_data **data,
414249ebf3fSBartosz Golaszewski 				   struct pwrseq_device *pwrseq,
415249ebf3fSBartosz Golaszewski 				   struct radix_tree_root *processed_units)
416249ebf3fSBartosz Golaszewski {
417249ebf3fSBartosz Golaszewski 	const struct pwrseq_target_data *pos;
418249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
419249ebf3fSBartosz Golaszewski 	int ret, i;
420249ebf3fSBartosz Golaszewski 
421249ebf3fSBartosz Golaszewski 	for (i = 0; data[i]; i++) {
422249ebf3fSBartosz Golaszewski 		pos = data[i];
423249ebf3fSBartosz Golaszewski 
424249ebf3fSBartosz Golaszewski 		ret = pwrseq_check_target_deps(pos);
425249ebf3fSBartosz Golaszewski 		if (ret)
426249ebf3fSBartosz Golaszewski 			return ret;
427249ebf3fSBartosz Golaszewski 
428249ebf3fSBartosz Golaszewski 		target = pwrseq_target_new(pos);
429249ebf3fSBartosz Golaszewski 		if (!target)
430249ebf3fSBartosz Golaszewski 			return -ENOMEM;
431249ebf3fSBartosz Golaszewski 
432249ebf3fSBartosz Golaszewski 		target->unit = pwrseq_unit_setup(pos->unit, &pwrseq->units,
433249ebf3fSBartosz Golaszewski 						 processed_units);
434249ebf3fSBartosz Golaszewski 		if (IS_ERR(target->unit)) {
435249ebf3fSBartosz Golaszewski 			ret = PTR_ERR(target->unit);
436249ebf3fSBartosz Golaszewski 			pwrseq_target_free(target);
437249ebf3fSBartosz Golaszewski 			return ret;
438249ebf3fSBartosz Golaszewski 		}
439249ebf3fSBartosz Golaszewski 
440249ebf3fSBartosz Golaszewski 		list_add_tail(&target->list, &pwrseq->targets);
441249ebf3fSBartosz Golaszewski 	}
442249ebf3fSBartosz Golaszewski 
443249ebf3fSBartosz Golaszewski 	return 0;
444249ebf3fSBartosz Golaszewski }
445249ebf3fSBartosz Golaszewski 
pwrseq_setup_targets(const struct pwrseq_target_data ** targets,struct pwrseq_device * pwrseq)446249ebf3fSBartosz Golaszewski static int pwrseq_setup_targets(const struct pwrseq_target_data **targets,
447249ebf3fSBartosz Golaszewski 				struct pwrseq_device *pwrseq)
448249ebf3fSBartosz Golaszewski {
449249ebf3fSBartosz Golaszewski 	struct radix_tree_root processed_units;
450249ebf3fSBartosz Golaszewski 	struct radix_tree_iter iter;
451249ebf3fSBartosz Golaszewski 	void __rcu **slot;
452249ebf3fSBartosz Golaszewski 	int ret;
453249ebf3fSBartosz Golaszewski 
454249ebf3fSBartosz Golaszewski 	INIT_RADIX_TREE(&processed_units, GFP_KERNEL);
455249ebf3fSBartosz Golaszewski 	ret = pwrseq_do_setup_targets(targets, pwrseq, &processed_units);
456249ebf3fSBartosz Golaszewski 	radix_tree_for_each_slot(slot, &processed_units, &iter, 0)
457249ebf3fSBartosz Golaszewski 		radix_tree_delete(&processed_units, iter.index);
458249ebf3fSBartosz Golaszewski 
459249ebf3fSBartosz Golaszewski 	return ret;
460249ebf3fSBartosz Golaszewski }
461249ebf3fSBartosz Golaszewski 
462249ebf3fSBartosz Golaszewski /**
463249ebf3fSBartosz Golaszewski  * pwrseq_device_register() - Register a new power sequencer.
464249ebf3fSBartosz Golaszewski  * @config: Configuration of the new power sequencing device.
465249ebf3fSBartosz Golaszewski  *
466249ebf3fSBartosz Golaszewski  * The config structure is only used during the call and can be freed after
467249ebf3fSBartosz Golaszewski  * the function returns. The config structure *must* have the parent device
468249ebf3fSBartosz Golaszewski  * as well as the match() callback and at least one target set.
469249ebf3fSBartosz Golaszewski  *
470249ebf3fSBartosz Golaszewski  * Returns:
471249ebf3fSBartosz Golaszewski  * Returns the address of the new pwrseq device or ERR_PTR() on failure.
472249ebf3fSBartosz Golaszewski  */
473249ebf3fSBartosz Golaszewski struct pwrseq_device *
pwrseq_device_register(const struct pwrseq_config * config)474249ebf3fSBartosz Golaszewski pwrseq_device_register(const struct pwrseq_config *config)
475249ebf3fSBartosz Golaszewski {
476249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq;
477249ebf3fSBartosz Golaszewski 	int ret, id;
478249ebf3fSBartosz Golaszewski 
479249ebf3fSBartosz Golaszewski 	if (!config->parent || !config->match || !config->targets ||
480249ebf3fSBartosz Golaszewski 	    !config->targets[0])
481249ebf3fSBartosz Golaszewski 		return ERR_PTR(-EINVAL);
482249ebf3fSBartosz Golaszewski 
483249ebf3fSBartosz Golaszewski 	pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
484249ebf3fSBartosz Golaszewski 	if (!pwrseq)
485249ebf3fSBartosz Golaszewski 		return ERR_PTR(-ENOMEM);
486249ebf3fSBartosz Golaszewski 
487249ebf3fSBartosz Golaszewski 	pwrseq->dev.type = &pwrseq_device_type;
488249ebf3fSBartosz Golaszewski 	pwrseq->dev.bus = &pwrseq_bus;
489249ebf3fSBartosz Golaszewski 	pwrseq->dev.parent = config->parent;
490249ebf3fSBartosz Golaszewski 	device_set_node(&pwrseq->dev, dev_fwnode(config->parent));
491249ebf3fSBartosz Golaszewski 	dev_set_drvdata(&pwrseq->dev, config->drvdata);
492249ebf3fSBartosz Golaszewski 
493249ebf3fSBartosz Golaszewski 	id = ida_alloc(&pwrseq_ida, GFP_KERNEL);
494249ebf3fSBartosz Golaszewski 	if (id < 0) {
495249ebf3fSBartosz Golaszewski 		kfree(pwrseq);
496249ebf3fSBartosz Golaszewski 		return ERR_PTR(id);
497249ebf3fSBartosz Golaszewski 	}
498249ebf3fSBartosz Golaszewski 
499249ebf3fSBartosz Golaszewski 	pwrseq->id = id;
500249ebf3fSBartosz Golaszewski 
501249ebf3fSBartosz Golaszewski 	/*
502249ebf3fSBartosz Golaszewski 	 * From this point onwards the device's release() callback is
503249ebf3fSBartosz Golaszewski 	 * responsible for freeing resources.
504249ebf3fSBartosz Golaszewski 	 */
505249ebf3fSBartosz Golaszewski 	device_initialize(&pwrseq->dev);
506249ebf3fSBartosz Golaszewski 
507249ebf3fSBartosz Golaszewski 	ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id);
508249ebf3fSBartosz Golaszewski 	if (ret)
509249ebf3fSBartosz Golaszewski 		goto err_put_pwrseq;
510249ebf3fSBartosz Golaszewski 
511249ebf3fSBartosz Golaszewski 	pwrseq->owner = config->owner ?: THIS_MODULE;
512249ebf3fSBartosz Golaszewski 	pwrseq->match = config->match;
513249ebf3fSBartosz Golaszewski 
514249ebf3fSBartosz Golaszewski 	init_rwsem(&pwrseq->rw_lock);
515249ebf3fSBartosz Golaszewski 	mutex_init(&pwrseq->state_lock);
516249ebf3fSBartosz Golaszewski 	INIT_LIST_HEAD(&pwrseq->targets);
517249ebf3fSBartosz Golaszewski 	INIT_LIST_HEAD(&pwrseq->units);
518249ebf3fSBartosz Golaszewski 
519249ebf3fSBartosz Golaszewski 	ret = pwrseq_setup_targets(config->targets, pwrseq);
520249ebf3fSBartosz Golaszewski 	if (ret)
521249ebf3fSBartosz Golaszewski 		goto err_put_pwrseq;
522249ebf3fSBartosz Golaszewski 
523249ebf3fSBartosz Golaszewski 	scoped_guard(rwsem_write, &pwrseq_sem) {
524249ebf3fSBartosz Golaszewski 		ret = device_add(&pwrseq->dev);
525249ebf3fSBartosz Golaszewski 		if (ret)
526249ebf3fSBartosz Golaszewski 			goto err_put_pwrseq;
527249ebf3fSBartosz Golaszewski 	}
528249ebf3fSBartosz Golaszewski 
529249ebf3fSBartosz Golaszewski 	return pwrseq;
530249ebf3fSBartosz Golaszewski 
531249ebf3fSBartosz Golaszewski err_put_pwrseq:
532249ebf3fSBartosz Golaszewski 	pwrseq_device_put(pwrseq);
533249ebf3fSBartosz Golaszewski 	return ERR_PTR(ret);
534249ebf3fSBartosz Golaszewski }
535249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_device_register);
536249ebf3fSBartosz Golaszewski 
537249ebf3fSBartosz Golaszewski /**
538249ebf3fSBartosz Golaszewski  * pwrseq_device_unregister() - Unregister the power sequencer.
539249ebf3fSBartosz Golaszewski  * @pwrseq: Power sequencer to unregister.
540249ebf3fSBartosz Golaszewski  */
pwrseq_device_unregister(struct pwrseq_device * pwrseq)541249ebf3fSBartosz Golaszewski void pwrseq_device_unregister(struct pwrseq_device *pwrseq)
542249ebf3fSBartosz Golaszewski {
543249ebf3fSBartosz Golaszewski 	struct device *dev = &pwrseq->dev;
544249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
545249ebf3fSBartosz Golaszewski 
546249ebf3fSBartosz Golaszewski 	scoped_guard(mutex, &pwrseq->state_lock) {
547249ebf3fSBartosz Golaszewski 		guard(rwsem_write)(&pwrseq->rw_lock);
548249ebf3fSBartosz Golaszewski 
549249ebf3fSBartosz Golaszewski 		list_for_each_entry(target, &pwrseq->targets, list)
550249ebf3fSBartosz Golaszewski 			WARN(target->unit->enable_count,
551249ebf3fSBartosz Golaszewski 			     "REMOVING POWER SEQUENCER WITH ACTIVE USERS\n");
552249ebf3fSBartosz Golaszewski 
553249ebf3fSBartosz Golaszewski 		guard(rwsem_write)(&pwrseq_sem);
554249ebf3fSBartosz Golaszewski 
555249ebf3fSBartosz Golaszewski 		device_del(dev);
556249ebf3fSBartosz Golaszewski 	}
557249ebf3fSBartosz Golaszewski 
558249ebf3fSBartosz Golaszewski 	pwrseq_device_put(pwrseq);
559249ebf3fSBartosz Golaszewski }
560249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_device_unregister);
561249ebf3fSBartosz Golaszewski 
devm_pwrseq_device_unregister(void * data)562249ebf3fSBartosz Golaszewski static void devm_pwrseq_device_unregister(void *data)
563249ebf3fSBartosz Golaszewski {
564249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq = data;
565249ebf3fSBartosz Golaszewski 
566249ebf3fSBartosz Golaszewski 	pwrseq_device_unregister(pwrseq);
567249ebf3fSBartosz Golaszewski }
568249ebf3fSBartosz Golaszewski 
569249ebf3fSBartosz Golaszewski /**
570249ebf3fSBartosz Golaszewski  * devm_pwrseq_device_register() - Managed variant of pwrseq_device_register().
571249ebf3fSBartosz Golaszewski  * @dev: Managing device.
572249ebf3fSBartosz Golaszewski  * @config: Configuration of the new power sequencing device.
573249ebf3fSBartosz Golaszewski  *
574249ebf3fSBartosz Golaszewski  * Returns:
575249ebf3fSBartosz Golaszewski  * Returns the address of the new pwrseq device or ERR_PTR() on failure.
576249ebf3fSBartosz Golaszewski  */
577249ebf3fSBartosz Golaszewski struct pwrseq_device *
devm_pwrseq_device_register(struct device * dev,const struct pwrseq_config * config)578249ebf3fSBartosz Golaszewski devm_pwrseq_device_register(struct device *dev,
579249ebf3fSBartosz Golaszewski 			    const struct pwrseq_config *config)
580249ebf3fSBartosz Golaszewski {
581249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq;
582249ebf3fSBartosz Golaszewski 	int ret;
583249ebf3fSBartosz Golaszewski 
584249ebf3fSBartosz Golaszewski 	pwrseq = pwrseq_device_register(config);
585249ebf3fSBartosz Golaszewski 	if (IS_ERR(pwrseq))
586249ebf3fSBartosz Golaszewski 		return pwrseq;
587249ebf3fSBartosz Golaszewski 
588249ebf3fSBartosz Golaszewski 	ret = devm_add_action_or_reset(dev, devm_pwrseq_device_unregister,
589249ebf3fSBartosz Golaszewski 				       pwrseq);
590249ebf3fSBartosz Golaszewski 	if (ret)
591249ebf3fSBartosz Golaszewski 		return ERR_PTR(ret);
592249ebf3fSBartosz Golaszewski 
593249ebf3fSBartosz Golaszewski 	return pwrseq;
594249ebf3fSBartosz Golaszewski }
595249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(devm_pwrseq_device_register);
596249ebf3fSBartosz Golaszewski 
597249ebf3fSBartosz Golaszewski /**
598249ebf3fSBartosz Golaszewski  * pwrseq_device_get_drvdata() - Get the driver private data associated with
599249ebf3fSBartosz Golaszewski  *                               this sequencer.
600249ebf3fSBartosz Golaszewski  * @pwrseq: Power sequencer object.
601249ebf3fSBartosz Golaszewski  *
602249ebf3fSBartosz Golaszewski  * Returns:
603249ebf3fSBartosz Golaszewski  * Address of the private driver data.
604249ebf3fSBartosz Golaszewski  */
pwrseq_device_get_drvdata(struct pwrseq_device * pwrseq)605249ebf3fSBartosz Golaszewski void *pwrseq_device_get_drvdata(struct pwrseq_device *pwrseq)
606249ebf3fSBartosz Golaszewski {
607249ebf3fSBartosz Golaszewski 	return dev_get_drvdata(&pwrseq->dev);
608249ebf3fSBartosz Golaszewski }
609249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_device_get_drvdata);
610249ebf3fSBartosz Golaszewski 
611249ebf3fSBartosz Golaszewski struct pwrseq_match_data {
612249ebf3fSBartosz Golaszewski 	struct pwrseq_desc *desc;
613249ebf3fSBartosz Golaszewski 	struct device *dev;
614249ebf3fSBartosz Golaszewski 	const char *target;
615249ebf3fSBartosz Golaszewski };
616249ebf3fSBartosz Golaszewski 
pwrseq_match_device(struct device * pwrseq_dev,void * data)617249ebf3fSBartosz Golaszewski static int pwrseq_match_device(struct device *pwrseq_dev, void *data)
618249ebf3fSBartosz Golaszewski {
619249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq = to_pwrseq_device(pwrseq_dev);
620249ebf3fSBartosz Golaszewski 	struct pwrseq_match_data *match_data = data;
621249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
622249ebf3fSBartosz Golaszewski 	int ret;
623249ebf3fSBartosz Golaszewski 
624249ebf3fSBartosz Golaszewski 	lockdep_assert_held_read(&pwrseq_sem);
625249ebf3fSBartosz Golaszewski 
626249ebf3fSBartosz Golaszewski 	guard(rwsem_read)(&pwrseq->rw_lock);
627249ebf3fSBartosz Golaszewski 	if (!device_is_registered(&pwrseq->dev))
628249ebf3fSBartosz Golaszewski 		return 0;
629249ebf3fSBartosz Golaszewski 
630249ebf3fSBartosz Golaszewski 	ret = pwrseq->match(pwrseq, match_data->dev);
631249ebf3fSBartosz Golaszewski 	if (ret <= 0)
632249ebf3fSBartosz Golaszewski 		return ret;
633249ebf3fSBartosz Golaszewski 
634249ebf3fSBartosz Golaszewski 	/* We got the matching device, let's find the right target. */
635249ebf3fSBartosz Golaszewski 	list_for_each_entry(target, &pwrseq->targets, list) {
636249ebf3fSBartosz Golaszewski 		if (strcmp(target->name, match_data->target))
637249ebf3fSBartosz Golaszewski 			continue;
638249ebf3fSBartosz Golaszewski 
639249ebf3fSBartosz Golaszewski 		match_data->desc->target = target;
640249ebf3fSBartosz Golaszewski 	}
641249ebf3fSBartosz Golaszewski 
642249ebf3fSBartosz Golaszewski 	/*
643249ebf3fSBartosz Golaszewski 	 * This device does not have this target. No point in deferring as it
644249ebf3fSBartosz Golaszewski 	 * will not get a new target dynamically later.
645249ebf3fSBartosz Golaszewski 	 */
646249ebf3fSBartosz Golaszewski 	if (!match_data->desc->target)
647249ebf3fSBartosz Golaszewski 		return -ENOENT;
648249ebf3fSBartosz Golaszewski 
649249ebf3fSBartosz Golaszewski 	if (!try_module_get(pwrseq->owner))
650249ebf3fSBartosz Golaszewski 		return -EPROBE_DEFER;
651249ebf3fSBartosz Golaszewski 
652249ebf3fSBartosz Golaszewski 	match_data->desc->pwrseq = pwrseq_device_get(pwrseq);
653249ebf3fSBartosz Golaszewski 
654249ebf3fSBartosz Golaszewski 	return 1;
655249ebf3fSBartosz Golaszewski }
656249ebf3fSBartosz Golaszewski 
657249ebf3fSBartosz Golaszewski /**
658249ebf3fSBartosz Golaszewski  * pwrseq_get() - Get the power sequencer associated with this device.
659249ebf3fSBartosz Golaszewski  * @dev: Device for which to get the sequencer.
660249ebf3fSBartosz Golaszewski  * @target: Name of the target exposed by the sequencer this device wants to
661249ebf3fSBartosz Golaszewski  *          reach.
662249ebf3fSBartosz Golaszewski  *
663249ebf3fSBartosz Golaszewski  * Returns:
664249ebf3fSBartosz Golaszewski  * New power sequencer descriptor for use by the consumer driver or ERR_PTR()
665249ebf3fSBartosz Golaszewski  * on failure.
666249ebf3fSBartosz Golaszewski  */
pwrseq_get(struct device * dev,const char * target)667249ebf3fSBartosz Golaszewski struct pwrseq_desc *pwrseq_get(struct device *dev, const char *target)
668249ebf3fSBartosz Golaszewski {
669249ebf3fSBartosz Golaszewski 	struct pwrseq_match_data match_data;
670249ebf3fSBartosz Golaszewski 	int ret;
671249ebf3fSBartosz Golaszewski 
672249ebf3fSBartosz Golaszewski 	struct pwrseq_desc *desc __free(kfree) = kzalloc(sizeof(*desc),
673249ebf3fSBartosz Golaszewski 							 GFP_KERNEL);
674249ebf3fSBartosz Golaszewski 	if (!desc)
675249ebf3fSBartosz Golaszewski 		return ERR_PTR(-ENOMEM);
676249ebf3fSBartosz Golaszewski 
677249ebf3fSBartosz Golaszewski 	match_data.desc = desc;
678249ebf3fSBartosz Golaszewski 	match_data.dev = dev;
679249ebf3fSBartosz Golaszewski 	match_data.target = target;
680249ebf3fSBartosz Golaszewski 
681249ebf3fSBartosz Golaszewski 	guard(rwsem_read)(&pwrseq_sem);
682249ebf3fSBartosz Golaszewski 
683249ebf3fSBartosz Golaszewski 	ret = bus_for_each_dev(&pwrseq_bus, NULL, &match_data,
684249ebf3fSBartosz Golaszewski 			       pwrseq_match_device);
685249ebf3fSBartosz Golaszewski 	if (ret < 0)
686249ebf3fSBartosz Golaszewski 		return ERR_PTR(ret);
687249ebf3fSBartosz Golaszewski 	if (ret == 0)
688249ebf3fSBartosz Golaszewski 		/* No device matched. */
689249ebf3fSBartosz Golaszewski 		return ERR_PTR(-EPROBE_DEFER);
690249ebf3fSBartosz Golaszewski 
691eba6d0f8SKrzysztof Kozlowski 	return_ptr(desc);
692249ebf3fSBartosz Golaszewski }
693249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_get);
694249ebf3fSBartosz Golaszewski 
695249ebf3fSBartosz Golaszewski /**
696249ebf3fSBartosz Golaszewski  * pwrseq_put() - Release the power sequencer descriptor.
697249ebf3fSBartosz Golaszewski  * @desc: Descriptor to release.
698249ebf3fSBartosz Golaszewski  */
pwrseq_put(struct pwrseq_desc * desc)699249ebf3fSBartosz Golaszewski void pwrseq_put(struct pwrseq_desc *desc)
700249ebf3fSBartosz Golaszewski {
701249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq;
702249ebf3fSBartosz Golaszewski 
703249ebf3fSBartosz Golaszewski 	if (!desc)
704249ebf3fSBartosz Golaszewski 		return;
705249ebf3fSBartosz Golaszewski 
706249ebf3fSBartosz Golaszewski 	pwrseq = desc->pwrseq;
707249ebf3fSBartosz Golaszewski 
708249ebf3fSBartosz Golaszewski 	if (desc->powered_on)
709249ebf3fSBartosz Golaszewski 		pwrseq_power_off(desc);
710249ebf3fSBartosz Golaszewski 
711249ebf3fSBartosz Golaszewski 	kfree(desc);
712249ebf3fSBartosz Golaszewski 	module_put(pwrseq->owner);
713249ebf3fSBartosz Golaszewski 	pwrseq_device_put(pwrseq);
714249ebf3fSBartosz Golaszewski }
715249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_put);
716249ebf3fSBartosz Golaszewski 
devm_pwrseq_put(void * data)717249ebf3fSBartosz Golaszewski static void devm_pwrseq_put(void *data)
718249ebf3fSBartosz Golaszewski {
719249ebf3fSBartosz Golaszewski 	struct pwrseq_desc *desc = data;
720249ebf3fSBartosz Golaszewski 
721249ebf3fSBartosz Golaszewski 	pwrseq_put(desc);
722249ebf3fSBartosz Golaszewski }
723249ebf3fSBartosz Golaszewski 
724249ebf3fSBartosz Golaszewski /**
725249ebf3fSBartosz Golaszewski  * devm_pwrseq_get() - Managed variant of pwrseq_get().
726249ebf3fSBartosz Golaszewski  * @dev: Device for which to get the sequencer and which also manages its
727249ebf3fSBartosz Golaszewski  *       lifetime.
728249ebf3fSBartosz Golaszewski  * @target: Name of the target exposed by the sequencer this device wants to
729249ebf3fSBartosz Golaszewski  *          reach.
730249ebf3fSBartosz Golaszewski  *
731249ebf3fSBartosz Golaszewski  * Returns:
732249ebf3fSBartosz Golaszewski  * New power sequencer descriptor for use by the consumer driver or ERR_PTR()
733249ebf3fSBartosz Golaszewski  * on failure.
734249ebf3fSBartosz Golaszewski  */
devm_pwrseq_get(struct device * dev,const char * target)735249ebf3fSBartosz Golaszewski struct pwrseq_desc *devm_pwrseq_get(struct device *dev, const char *target)
736249ebf3fSBartosz Golaszewski {
737249ebf3fSBartosz Golaszewski 	struct pwrseq_desc *desc;
738249ebf3fSBartosz Golaszewski 	int ret;
739249ebf3fSBartosz Golaszewski 
740249ebf3fSBartosz Golaszewski 	desc = pwrseq_get(dev, target);
741249ebf3fSBartosz Golaszewski 	if (IS_ERR(desc))
742249ebf3fSBartosz Golaszewski 		return desc;
743249ebf3fSBartosz Golaszewski 
744249ebf3fSBartosz Golaszewski 	ret = devm_add_action_or_reset(dev, devm_pwrseq_put, desc);
745249ebf3fSBartosz Golaszewski 	if (ret)
746249ebf3fSBartosz Golaszewski 		return ERR_PTR(ret);
747249ebf3fSBartosz Golaszewski 
748249ebf3fSBartosz Golaszewski 	return desc;
749249ebf3fSBartosz Golaszewski }
750249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(devm_pwrseq_get);
751249ebf3fSBartosz Golaszewski 
752249ebf3fSBartosz Golaszewski static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,
753249ebf3fSBartosz Golaszewski 			      struct pwrseq_unit *target);
754249ebf3fSBartosz Golaszewski static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,
755249ebf3fSBartosz Golaszewski 			       struct pwrseq_unit *target);
756249ebf3fSBartosz Golaszewski 
pwrseq_unit_enable_deps(struct pwrseq_device * pwrseq,struct list_head * list)757249ebf3fSBartosz Golaszewski static int pwrseq_unit_enable_deps(struct pwrseq_device *pwrseq,
758249ebf3fSBartosz Golaszewski 				   struct list_head *list)
759249ebf3fSBartosz Golaszewski {
760249ebf3fSBartosz Golaszewski 	struct pwrseq_unit_dep *pos;
761249ebf3fSBartosz Golaszewski 	int ret = 0;
762249ebf3fSBartosz Golaszewski 
763249ebf3fSBartosz Golaszewski 	list_for_each_entry(pos, list, list) {
764249ebf3fSBartosz Golaszewski 		ret = pwrseq_unit_enable(pwrseq, pos->unit);
765249ebf3fSBartosz Golaszewski 		if (ret) {
766249ebf3fSBartosz Golaszewski 			list_for_each_entry_continue_reverse(pos, list, list)
767249ebf3fSBartosz Golaszewski 				pwrseq_unit_disable(pwrseq, pos->unit);
768249ebf3fSBartosz Golaszewski 			break;
769249ebf3fSBartosz Golaszewski 		}
770249ebf3fSBartosz Golaszewski 	}
771249ebf3fSBartosz Golaszewski 
772249ebf3fSBartosz Golaszewski 	return ret;
773249ebf3fSBartosz Golaszewski }
774249ebf3fSBartosz Golaszewski 
pwrseq_unit_disable_deps(struct pwrseq_device * pwrseq,struct list_head * list)775249ebf3fSBartosz Golaszewski static int pwrseq_unit_disable_deps(struct pwrseq_device *pwrseq,
776249ebf3fSBartosz Golaszewski 				    struct list_head *list)
777249ebf3fSBartosz Golaszewski {
778249ebf3fSBartosz Golaszewski 	struct pwrseq_unit_dep *pos;
779249ebf3fSBartosz Golaszewski 	int ret = 0;
780249ebf3fSBartosz Golaszewski 
781249ebf3fSBartosz Golaszewski 	list_for_each_entry_reverse(pos, list, list) {
782249ebf3fSBartosz Golaszewski 		ret = pwrseq_unit_disable(pwrseq, pos->unit);
783249ebf3fSBartosz Golaszewski 		if (ret) {
784249ebf3fSBartosz Golaszewski 			list_for_each_entry_continue(pos, list, list)
785249ebf3fSBartosz Golaszewski 				pwrseq_unit_enable(pwrseq, pos->unit);
786249ebf3fSBartosz Golaszewski 			break;
787249ebf3fSBartosz Golaszewski 		}
788249ebf3fSBartosz Golaszewski 	}
789249ebf3fSBartosz Golaszewski 
790249ebf3fSBartosz Golaszewski 	return ret;
791249ebf3fSBartosz Golaszewski }
792249ebf3fSBartosz Golaszewski 
pwrseq_unit_enable(struct pwrseq_device * pwrseq,struct pwrseq_unit * unit)793249ebf3fSBartosz Golaszewski static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,
794249ebf3fSBartosz Golaszewski 			      struct pwrseq_unit *unit)
795249ebf3fSBartosz Golaszewski {
796249ebf3fSBartosz Golaszewski 	int ret;
797249ebf3fSBartosz Golaszewski 
798249ebf3fSBartosz Golaszewski 	lockdep_assert_held_read(&pwrseq->rw_lock);
799249ebf3fSBartosz Golaszewski 	lockdep_assert_held(&pwrseq->state_lock);
800249ebf3fSBartosz Golaszewski 
801249ebf3fSBartosz Golaszewski 	if (unit->enable_count != 0) {
802249ebf3fSBartosz Golaszewski 		unit->enable_count++;
803249ebf3fSBartosz Golaszewski 		return 0;
804249ebf3fSBartosz Golaszewski 	}
805249ebf3fSBartosz Golaszewski 
806249ebf3fSBartosz Golaszewski 	ret = pwrseq_unit_enable_deps(pwrseq, &unit->deps);
807249ebf3fSBartosz Golaszewski 	if (ret) {
808249ebf3fSBartosz Golaszewski 		dev_err(&pwrseq->dev,
809249ebf3fSBartosz Golaszewski 			"Failed to enable dependencies before power-on for target '%s': %d\n",
810249ebf3fSBartosz Golaszewski 			unit->name, ret);
811249ebf3fSBartosz Golaszewski 		return ret;
812249ebf3fSBartosz Golaszewski 	}
813249ebf3fSBartosz Golaszewski 
814249ebf3fSBartosz Golaszewski 	if (unit->enable) {
815249ebf3fSBartosz Golaszewski 		ret = unit->enable(pwrseq);
816249ebf3fSBartosz Golaszewski 		if (ret) {
817249ebf3fSBartosz Golaszewski 			dev_err(&pwrseq->dev,
818249ebf3fSBartosz Golaszewski 				"Failed to enable target '%s': %d\n",
819249ebf3fSBartosz Golaszewski 				unit->name, ret);
820249ebf3fSBartosz Golaszewski 			pwrseq_unit_disable_deps(pwrseq, &unit->deps);
821249ebf3fSBartosz Golaszewski 			return ret;
822249ebf3fSBartosz Golaszewski 		}
823249ebf3fSBartosz Golaszewski 	}
824249ebf3fSBartosz Golaszewski 
825249ebf3fSBartosz Golaszewski 	unit->enable_count++;
826249ebf3fSBartosz Golaszewski 
827249ebf3fSBartosz Golaszewski 	return 0;
828249ebf3fSBartosz Golaszewski }
829249ebf3fSBartosz Golaszewski 
pwrseq_unit_disable(struct pwrseq_device * pwrseq,struct pwrseq_unit * unit)830249ebf3fSBartosz Golaszewski static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,
831249ebf3fSBartosz Golaszewski 			       struct pwrseq_unit *unit)
832249ebf3fSBartosz Golaszewski {
833249ebf3fSBartosz Golaszewski 	int ret;
834249ebf3fSBartosz Golaszewski 
835249ebf3fSBartosz Golaszewski 	lockdep_assert_held_read(&pwrseq->rw_lock);
836249ebf3fSBartosz Golaszewski 	lockdep_assert_held(&pwrseq->state_lock);
837249ebf3fSBartosz Golaszewski 
838249ebf3fSBartosz Golaszewski 	if (unit->enable_count == 0) {
839249ebf3fSBartosz Golaszewski 		WARN(1, "Unmatched power-off for target '%s'\n",
840249ebf3fSBartosz Golaszewski 		     unit->name);
841249ebf3fSBartosz Golaszewski 		return -EBUSY;
842249ebf3fSBartosz Golaszewski 	}
843249ebf3fSBartosz Golaszewski 
844249ebf3fSBartosz Golaszewski 	if (unit->enable_count != 1) {
845249ebf3fSBartosz Golaszewski 		unit->enable_count--;
846249ebf3fSBartosz Golaszewski 		return 0;
847249ebf3fSBartosz Golaszewski 	}
848249ebf3fSBartosz Golaszewski 
849249ebf3fSBartosz Golaszewski 	if (unit->disable) {
850249ebf3fSBartosz Golaszewski 		ret = unit->disable(pwrseq);
851249ebf3fSBartosz Golaszewski 		if (ret) {
852249ebf3fSBartosz Golaszewski 			dev_err(&pwrseq->dev,
853249ebf3fSBartosz Golaszewski 				"Failed to disable target '%s': %d\n",
854249ebf3fSBartosz Golaszewski 				unit->name, ret);
855249ebf3fSBartosz Golaszewski 			return ret;
856249ebf3fSBartosz Golaszewski 		}
857249ebf3fSBartosz Golaszewski 	}
858249ebf3fSBartosz Golaszewski 
859249ebf3fSBartosz Golaszewski 	ret = pwrseq_unit_disable_deps(pwrseq, &unit->deps);
860249ebf3fSBartosz Golaszewski 	if (ret) {
861249ebf3fSBartosz Golaszewski 		dev_err(&pwrseq->dev,
862249ebf3fSBartosz Golaszewski 			"Failed to disable dependencies after power-off for target '%s': %d\n",
863249ebf3fSBartosz Golaszewski 			unit->name, ret);
864249ebf3fSBartosz Golaszewski 		if (unit->enable)
865249ebf3fSBartosz Golaszewski 			unit->enable(pwrseq);
866249ebf3fSBartosz Golaszewski 		return ret;
867249ebf3fSBartosz Golaszewski 	}
868249ebf3fSBartosz Golaszewski 
869249ebf3fSBartosz Golaszewski 	unit->enable_count--;
870249ebf3fSBartosz Golaszewski 
871249ebf3fSBartosz Golaszewski 	return 0;
872249ebf3fSBartosz Golaszewski }
873249ebf3fSBartosz Golaszewski 
874249ebf3fSBartosz Golaszewski /**
875249ebf3fSBartosz Golaszewski  * pwrseq_power_on() - Issue a power-on request on behalf of the consumer
876249ebf3fSBartosz Golaszewski  *                     device.
877249ebf3fSBartosz Golaszewski  * @desc: Descriptor referencing the power sequencer.
878249ebf3fSBartosz Golaszewski  *
879249ebf3fSBartosz Golaszewski  * This function tells the power sequencer that the consumer wants to be
880249ebf3fSBartosz Golaszewski  * powered-up. The sequencer may already have powered-up the device in which
881249ebf3fSBartosz Golaszewski  * case the function returns 0. If the power-up sequence is already in
882249ebf3fSBartosz Golaszewski  * progress, the function will block until it's done and return 0. If this is
883249ebf3fSBartosz Golaszewski  * the first request, the device will be powered up.
884249ebf3fSBartosz Golaszewski  *
885249ebf3fSBartosz Golaszewski  * Returns:
886249ebf3fSBartosz Golaszewski  * 0 on success, negative error number on failure.
887249ebf3fSBartosz Golaszewski  */
pwrseq_power_on(struct pwrseq_desc * desc)888249ebf3fSBartosz Golaszewski int pwrseq_power_on(struct pwrseq_desc *desc)
889249ebf3fSBartosz Golaszewski {
890249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq;
891249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
892249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
893249ebf3fSBartosz Golaszewski 	int ret;
894249ebf3fSBartosz Golaszewski 
895249ebf3fSBartosz Golaszewski 	might_sleep();
896249ebf3fSBartosz Golaszewski 
897249ebf3fSBartosz Golaszewski 	if (!desc || desc->powered_on)
898249ebf3fSBartosz Golaszewski 		return 0;
899249ebf3fSBartosz Golaszewski 
900249ebf3fSBartosz Golaszewski 	pwrseq = desc->pwrseq;
901249ebf3fSBartosz Golaszewski 	target = desc->target;
902249ebf3fSBartosz Golaszewski 	unit = target->unit;
903249ebf3fSBartosz Golaszewski 
904249ebf3fSBartosz Golaszewski 	guard(rwsem_read)(&pwrseq->rw_lock);
905249ebf3fSBartosz Golaszewski 	if (!device_is_registered(&pwrseq->dev))
906249ebf3fSBartosz Golaszewski 		return -ENODEV;
907249ebf3fSBartosz Golaszewski 
908249ebf3fSBartosz Golaszewski 	scoped_guard(mutex, &pwrseq->state_lock) {
909249ebf3fSBartosz Golaszewski 		ret = pwrseq_unit_enable(pwrseq, unit);
910249ebf3fSBartosz Golaszewski 		if (!ret)
911249ebf3fSBartosz Golaszewski 			desc->powered_on = true;
912249ebf3fSBartosz Golaszewski 	}
913249ebf3fSBartosz Golaszewski 
914249ebf3fSBartosz Golaszewski 	if (target->post_enable) {
915249ebf3fSBartosz Golaszewski 		ret = target->post_enable(pwrseq);
916249ebf3fSBartosz Golaszewski 		if (ret) {
917249ebf3fSBartosz Golaszewski 			pwrseq_unit_disable(pwrseq, unit);
918249ebf3fSBartosz Golaszewski 			desc->powered_on = false;
919249ebf3fSBartosz Golaszewski 		}
920249ebf3fSBartosz Golaszewski 	}
921249ebf3fSBartosz Golaszewski 
922249ebf3fSBartosz Golaszewski 	return ret;
923249ebf3fSBartosz Golaszewski }
924249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_power_on);
925249ebf3fSBartosz Golaszewski 
926249ebf3fSBartosz Golaszewski /**
927249ebf3fSBartosz Golaszewski  * pwrseq_power_off() - Issue a power-off request on behalf of the consumer
928249ebf3fSBartosz Golaszewski  *                      device.
929249ebf3fSBartosz Golaszewski  * @desc: Descriptor referencing the power sequencer.
930249ebf3fSBartosz Golaszewski  *
931249ebf3fSBartosz Golaszewski  * This undoes the effects of pwrseq_power_on(). It issues a power-off request
932249ebf3fSBartosz Golaszewski  * on behalf of the consumer and when the last remaining user does so, the
933249ebf3fSBartosz Golaszewski  * power-down sequence will be started. If one is in progress, the function
934249ebf3fSBartosz Golaszewski  * will block until it's complete and then return.
935249ebf3fSBartosz Golaszewski  *
936249ebf3fSBartosz Golaszewski  * Returns:
937249ebf3fSBartosz Golaszewski  * 0 on success, negative error number on failure.
938249ebf3fSBartosz Golaszewski  */
pwrseq_power_off(struct pwrseq_desc * desc)939249ebf3fSBartosz Golaszewski int pwrseq_power_off(struct pwrseq_desc *desc)
940249ebf3fSBartosz Golaszewski {
941249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq;
942249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
943249ebf3fSBartosz Golaszewski 	int ret;
944249ebf3fSBartosz Golaszewski 
945249ebf3fSBartosz Golaszewski 	might_sleep();
946249ebf3fSBartosz Golaszewski 
947249ebf3fSBartosz Golaszewski 	if (!desc || !desc->powered_on)
948249ebf3fSBartosz Golaszewski 		return 0;
949249ebf3fSBartosz Golaszewski 
950249ebf3fSBartosz Golaszewski 	pwrseq = desc->pwrseq;
951249ebf3fSBartosz Golaszewski 	unit = desc->target->unit;
952249ebf3fSBartosz Golaszewski 
953249ebf3fSBartosz Golaszewski 	guard(rwsem_read)(&pwrseq->rw_lock);
954249ebf3fSBartosz Golaszewski 	if (!device_is_registered(&pwrseq->dev))
955249ebf3fSBartosz Golaszewski 		return -ENODEV;
956249ebf3fSBartosz Golaszewski 
957249ebf3fSBartosz Golaszewski 	guard(mutex)(&pwrseq->state_lock);
958249ebf3fSBartosz Golaszewski 
959249ebf3fSBartosz Golaszewski 	ret = pwrseq_unit_disable(pwrseq, unit);
960249ebf3fSBartosz Golaszewski 	if (!ret)
961249ebf3fSBartosz Golaszewski 		desc->powered_on = false;
962249ebf3fSBartosz Golaszewski 
963249ebf3fSBartosz Golaszewski 	return ret;
964249ebf3fSBartosz Golaszewski }
965249ebf3fSBartosz Golaszewski EXPORT_SYMBOL_GPL(pwrseq_power_off);
966249ebf3fSBartosz Golaszewski 
967249ebf3fSBartosz Golaszewski #if IS_ENABLED(CONFIG_DEBUG_FS)
968249ebf3fSBartosz Golaszewski 
969249ebf3fSBartosz Golaszewski struct pwrseq_debugfs_count_ctx {
970249ebf3fSBartosz Golaszewski 	struct device *dev;
971249ebf3fSBartosz Golaszewski 	loff_t index;
972249ebf3fSBartosz Golaszewski };
973249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_count(struct device * dev,void * data)974249ebf3fSBartosz Golaszewski static int pwrseq_debugfs_seq_count(struct device *dev, void *data)
975249ebf3fSBartosz Golaszewski {
976249ebf3fSBartosz Golaszewski 	struct pwrseq_debugfs_count_ctx *ctx = data;
977249ebf3fSBartosz Golaszewski 
978249ebf3fSBartosz Golaszewski 	ctx->dev = dev;
979249ebf3fSBartosz Golaszewski 
980249ebf3fSBartosz Golaszewski 	return ctx->index-- ? 0 : 1;
981249ebf3fSBartosz Golaszewski }
982249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_start(struct seq_file * seq,loff_t * pos)983249ebf3fSBartosz Golaszewski static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos)
984249ebf3fSBartosz Golaszewski {
985249ebf3fSBartosz Golaszewski 	struct pwrseq_debugfs_count_ctx ctx;
986249ebf3fSBartosz Golaszewski 
987249ebf3fSBartosz Golaszewski 	ctx.dev = NULL;
988249ebf3fSBartosz Golaszewski 	ctx.index = *pos;
989249ebf3fSBartosz Golaszewski 
990249ebf3fSBartosz Golaszewski 	/*
991249ebf3fSBartosz Golaszewski 	 * We're holding the lock for the entire printout so no need to fiddle
992249ebf3fSBartosz Golaszewski 	 * with device reference count.
993249ebf3fSBartosz Golaszewski 	 */
994249ebf3fSBartosz Golaszewski 	down_read(&pwrseq_sem);
995249ebf3fSBartosz Golaszewski 
996249ebf3fSBartosz Golaszewski 	bus_for_each_dev(&pwrseq_bus, NULL, &ctx, pwrseq_debugfs_seq_count);
997249ebf3fSBartosz Golaszewski 	if (!ctx.index)
998249ebf3fSBartosz Golaszewski 		return NULL;
999249ebf3fSBartosz Golaszewski 
1000249ebf3fSBartosz Golaszewski 	return ctx.dev;
1001249ebf3fSBartosz Golaszewski }
1002249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_next(struct seq_file * seq,void * data,loff_t * pos)1003249ebf3fSBartosz Golaszewski static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data,
1004249ebf3fSBartosz Golaszewski 				     loff_t *pos)
1005249ebf3fSBartosz Golaszewski {
1006249ebf3fSBartosz Golaszewski 	struct device *curr = data;
1007249ebf3fSBartosz Golaszewski 
1008249ebf3fSBartosz Golaszewski 	++*pos;
1009249ebf3fSBartosz Golaszewski 
1010249ebf3fSBartosz Golaszewski 	struct device *next __free(put_device) =
1011249ebf3fSBartosz Golaszewski 			bus_find_next_device(&pwrseq_bus, curr);
1012249ebf3fSBartosz Golaszewski 	return next;
1013249ebf3fSBartosz Golaszewski }
1014249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_show_target(struct seq_file * seq,struct pwrseq_target * target)1015249ebf3fSBartosz Golaszewski static void pwrseq_debugfs_seq_show_target(struct seq_file *seq,
1016249ebf3fSBartosz Golaszewski 					   struct pwrseq_target *target)
1017249ebf3fSBartosz Golaszewski {
1018249ebf3fSBartosz Golaszewski 	seq_printf(seq, "    target: [%s] (target unit: [%s])\n",
1019249ebf3fSBartosz Golaszewski 		   target->name, target->unit->name);
1020249ebf3fSBartosz Golaszewski }
1021249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_show_unit(struct seq_file * seq,struct pwrseq_unit * unit)1022249ebf3fSBartosz Golaszewski static void pwrseq_debugfs_seq_show_unit(struct seq_file *seq,
1023249ebf3fSBartosz Golaszewski 					 struct pwrseq_unit *unit)
1024249ebf3fSBartosz Golaszewski {
1025249ebf3fSBartosz Golaszewski 	struct pwrseq_unit_dep *ref;
1026249ebf3fSBartosz Golaszewski 
1027249ebf3fSBartosz Golaszewski 	seq_printf(seq, "    unit: [%s] - enable count: %u\n",
1028249ebf3fSBartosz Golaszewski 		   unit->name, unit->enable_count);
1029249ebf3fSBartosz Golaszewski 
1030249ebf3fSBartosz Golaszewski 	if (list_empty(&unit->deps))
1031249ebf3fSBartosz Golaszewski 		return;
1032249ebf3fSBartosz Golaszewski 
1033249ebf3fSBartosz Golaszewski 	seq_puts(seq, "      dependencies:\n");
1034249ebf3fSBartosz Golaszewski 	list_for_each_entry(ref, &unit->deps, list)
1035249ebf3fSBartosz Golaszewski 		seq_printf(seq, "        [%s]\n", ref->unit->name);
1036249ebf3fSBartosz Golaszewski }
1037249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_show(struct seq_file * seq,void * data)1038249ebf3fSBartosz Golaszewski static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data)
1039249ebf3fSBartosz Golaszewski {
1040249ebf3fSBartosz Golaszewski 	struct device *dev = data;
1041249ebf3fSBartosz Golaszewski 	struct pwrseq_device *pwrseq = to_pwrseq_device(dev);
1042249ebf3fSBartosz Golaszewski 	struct pwrseq_target *target;
1043249ebf3fSBartosz Golaszewski 	struct pwrseq_unit *unit;
1044249ebf3fSBartosz Golaszewski 
1045249ebf3fSBartosz Golaszewski 	seq_printf(seq, "%s:\n", dev_name(dev));
1046249ebf3fSBartosz Golaszewski 
1047249ebf3fSBartosz Golaszewski 	seq_puts(seq, "  targets:\n");
1048249ebf3fSBartosz Golaszewski 	list_for_each_entry(target, &pwrseq->targets, list)
1049249ebf3fSBartosz Golaszewski 		pwrseq_debugfs_seq_show_target(seq, target);
1050249ebf3fSBartosz Golaszewski 
1051249ebf3fSBartosz Golaszewski 	seq_puts(seq, "  units:\n");
1052249ebf3fSBartosz Golaszewski 	list_for_each_entry(unit, &pwrseq->units, list)
1053249ebf3fSBartosz Golaszewski 		pwrseq_debugfs_seq_show_unit(seq, unit);
1054249ebf3fSBartosz Golaszewski 
1055249ebf3fSBartosz Golaszewski 	return 0;
1056249ebf3fSBartosz Golaszewski }
1057249ebf3fSBartosz Golaszewski 
pwrseq_debugfs_seq_stop(struct seq_file * seq,void * data)1058249ebf3fSBartosz Golaszewski static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data)
1059249ebf3fSBartosz Golaszewski {
1060249ebf3fSBartosz Golaszewski 	up_read(&pwrseq_sem);
1061249ebf3fSBartosz Golaszewski }
1062249ebf3fSBartosz Golaszewski 
1063249ebf3fSBartosz Golaszewski static const struct seq_operations pwrseq_debugfs_sops = {
1064249ebf3fSBartosz Golaszewski 	.start = pwrseq_debugfs_seq_start,
1065249ebf3fSBartosz Golaszewski 	.next = pwrseq_debugfs_seq_next,
1066249ebf3fSBartosz Golaszewski 	.show = pwrseq_debugfs_seq_show,
1067249ebf3fSBartosz Golaszewski 	.stop = pwrseq_debugfs_seq_stop,
1068249ebf3fSBartosz Golaszewski };
1069249ebf3fSBartosz Golaszewski DEFINE_SEQ_ATTRIBUTE(pwrseq_debugfs);
1070249ebf3fSBartosz Golaszewski 
1071249ebf3fSBartosz Golaszewski static struct dentry *pwrseq_debugfs_dentry;
1072249ebf3fSBartosz Golaszewski 
1073249ebf3fSBartosz Golaszewski #endif /* CONFIG_DEBUG_FS */
1074249ebf3fSBartosz Golaszewski 
pwrseq_init(void)1075249ebf3fSBartosz Golaszewski static int __init pwrseq_init(void)
1076249ebf3fSBartosz Golaszewski {
1077249ebf3fSBartosz Golaszewski 	int ret;
1078249ebf3fSBartosz Golaszewski 
1079249ebf3fSBartosz Golaszewski 	ret = bus_register(&pwrseq_bus);
1080249ebf3fSBartosz Golaszewski 	if (ret) {
1081249ebf3fSBartosz Golaszewski 		pr_err("Failed to register the power sequencer bus\n");
1082249ebf3fSBartosz Golaszewski 		return ret;
1083249ebf3fSBartosz Golaszewski 	}
1084249ebf3fSBartosz Golaszewski 
1085249ebf3fSBartosz Golaszewski #if IS_ENABLED(CONFIG_DEBUG_FS)
1086249ebf3fSBartosz Golaszewski 	pwrseq_debugfs_dentry = debugfs_create_file("pwrseq", 0444, NULL, NULL,
1087249ebf3fSBartosz Golaszewski 						    &pwrseq_debugfs_fops);
1088249ebf3fSBartosz Golaszewski #endif  /* CONFIG_DEBUG_FS */
1089249ebf3fSBartosz Golaszewski 
1090249ebf3fSBartosz Golaszewski 	return 0;
1091249ebf3fSBartosz Golaszewski }
1092249ebf3fSBartosz Golaszewski subsys_initcall(pwrseq_init);
1093249ebf3fSBartosz Golaszewski 
pwrseq_exit(void)1094249ebf3fSBartosz Golaszewski static void __exit pwrseq_exit(void)
1095249ebf3fSBartosz Golaszewski {
1096249ebf3fSBartosz Golaszewski #if IS_ENABLED(CONFIG_DEBUG_FS)
1097249ebf3fSBartosz Golaszewski 	debugfs_remove_recursive(pwrseq_debugfs_dentry);
1098249ebf3fSBartosz Golaszewski #endif  /* CONFIG_DEBUG_FS */
1099249ebf3fSBartosz Golaszewski 
1100249ebf3fSBartosz Golaszewski 	bus_unregister(&pwrseq_bus);
1101249ebf3fSBartosz Golaszewski }
1102249ebf3fSBartosz Golaszewski module_exit(pwrseq_exit);
1103249ebf3fSBartosz Golaszewski 
1104249ebf3fSBartosz Golaszewski MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
1105249ebf3fSBartosz Golaszewski MODULE_DESCRIPTION("Power Sequencing subsystem core");
1106249ebf3fSBartosz Golaszewski MODULE_LICENSE("GPL");
1107