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