| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2024 Linaro Ltd.
- */
- #include <linux/bug.h>
- #include <linux/cleanup.h>
- #include <linux/debugfs.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/export.h>
- #include <linux/idr.h>
- #include <linux/kernel.h>
- #include <linux/kref.h>
- #include <linux/list.h>
- #include <linux/lockdep.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/property.h>
- #include <linux/pwrseq/consumer.h>
- #include <linux/pwrseq/provider.h>
- #include <linux/radix-tree.h>
- #include <linux/rwsem.h>
- #include <linux/slab.h>
- /*
- * Power-sequencing framework for linux.
- *
- * This subsystem allows power sequence providers to register a set of targets
- * that consumers may request and power-up/down.
- *
- * Glossary:
- *
- * Unit - a unit is a discreet chunk of a power sequence. For instance one unit
- * may enable a set of regulators, another may enable a specific GPIO. Units
- * can define dependencies in the form of other units that must be enabled
- * before it itself can be.
- *
- * Target - a target is a set of units (composed of the "final" unit and its
- * dependencies) that a consumer selects by its name when requesting a handle
- * to the power sequencer. Via the dependency system, multiple targets may
- * share the same parts of a power sequence but ignore parts that are
- * irrelevant.
- *
- * Descriptor - a handle passed by the pwrseq core to every consumer that
- * serves as the entry point to the provider layer. It ensures coherence
- * between different users and keeps reference counting consistent.
- *
- * Each provider must define a .match() callback whose role is to determine
- * whether a potential consumer is in fact associated with this sequencer.
- * This allows creating abstraction layers on top of regular device-tree
- * resources like regulators, clocks and other nodes connected to the consumer
- * via phandle.
- */
- static DEFINE_IDA(pwrseq_ida);
- /*
- * Protects the device list on the pwrseq bus from concurrent modifications
- * but allows simultaneous read-only access.
- */
- static DECLARE_RWSEM(pwrseq_sem);
- /**
- * struct pwrseq_unit - Private power-sequence unit data.
- * @ref: Reference count for this object. When it goes to 0, the object is
- * destroyed.
- * @name: Name of this target.
- * @list: Link to siblings on the list of all units of a single sequencer.
- * @deps: List of units on which this unit depends.
- * @enable: Callback running the part of the power-on sequence provided by
- * this unit.
- * @disable: Callback running the part of the power-off sequence provided
- * by this unit.
- * @enable_count: Current number of users that enabled this unit. May be the
- * consumer of the power sequencer or other units that depend
- * on this one.
- */
- struct pwrseq_unit {
- struct kref ref;
- const char *name;
- struct list_head list;
- struct list_head deps;
- pwrseq_power_state_func enable;
- pwrseq_power_state_func disable;
- unsigned int enable_count;
- };
- static struct pwrseq_unit *pwrseq_unit_new(const struct pwrseq_unit_data *data)
- {
- struct pwrseq_unit *unit;
- unit = kzalloc(sizeof(*unit), GFP_KERNEL);
- if (!unit)
- return NULL;
- unit->name = kstrdup_const(data->name, GFP_KERNEL);
- if (!unit->name) {
- kfree(unit);
- return NULL;
- }
- kref_init(&unit->ref);
- INIT_LIST_HEAD(&unit->deps);
- unit->enable = data->enable;
- unit->disable = data->disable;
- return unit;
- }
- static struct pwrseq_unit *pwrseq_unit_get(struct pwrseq_unit *unit)
- {
- kref_get(&unit->ref);
- return unit;
- }
- static void pwrseq_unit_release(struct kref *ref);
- static void pwrseq_unit_put(struct pwrseq_unit *unit)
- {
- kref_put(&unit->ref, pwrseq_unit_release);
- }
- /**
- * struct pwrseq_unit_dep - Wrapper around a reference to the unit structure
- * allowing to keep it on multiple dependency lists
- * in different units.
- * @list: Siblings on the list.
- * @unit: Address of the referenced unit.
- */
- struct pwrseq_unit_dep {
- struct list_head list;
- struct pwrseq_unit *unit;
- };
- static struct pwrseq_unit_dep *pwrseq_unit_dep_new(struct pwrseq_unit *unit)
- {
- struct pwrseq_unit_dep *dep;
- dep = kzalloc(sizeof(*dep), GFP_KERNEL);
- if (!dep)
- return NULL;
- dep->unit = unit;
- return dep;
- }
- static void pwrseq_unit_dep_free(struct pwrseq_unit_dep *ref)
- {
- pwrseq_unit_put(ref->unit);
- kfree(ref);
- }
- static void pwrseq_unit_free_deps(struct list_head *list)
- {
- struct pwrseq_unit_dep *dep, *next;
- list_for_each_entry_safe(dep, next, list, list) {
- list_del(&dep->list);
- pwrseq_unit_dep_free(dep);
- }
- }
- static void pwrseq_unit_release(struct kref *ref)
- {
- struct pwrseq_unit *unit = container_of(ref, struct pwrseq_unit, ref);
- pwrseq_unit_free_deps(&unit->deps);
- list_del(&unit->list);
- kfree_const(unit->name);
- kfree(unit);
- }
- /**
- * struct pwrseq_target - Private power-sequence target data.
- * @list: Siblings on the list of all targets exposed by a power sequencer.
- * @name: Name of the target.
- * @unit: Final unit for this target.
- * @post_enable: Callback run after the target unit has been enabled, *after*
- * the state lock has been released. It's useful for implementing
- * boot-up delays without blocking other users from powering up
- * using the same power sequencer.
- */
- struct pwrseq_target {
- struct list_head list;
- const char *name;
- struct pwrseq_unit *unit;
- pwrseq_power_state_func post_enable;
- };
- static struct pwrseq_target *
- pwrseq_target_new(const struct pwrseq_target_data *data)
- {
- struct pwrseq_target *target;
- target = kzalloc(sizeof(*target), GFP_KERNEL);
- if (!target)
- return NULL;
- target->name = kstrdup_const(data->name, GFP_KERNEL);
- if (!target->name) {
- kfree(target);
- return NULL;
- }
- target->post_enable = data->post_enable;
- return target;
- }
- static void pwrseq_target_free(struct pwrseq_target *target)
- {
- if (!IS_ERR_OR_NULL(target->unit))
- pwrseq_unit_put(target->unit);
- kfree_const(target->name);
- kfree(target);
- }
- /**
- * struct pwrseq_device - Private power sequencing data.
- * @dev: Device struct associated with this sequencer.
- * @id: Device ID.
- * @owner: Prevents removal of active power sequencing providers.
- * @rw_lock: Protects the device from being unregistered while in use.
- * @state_lock: Prevents multiple users running the power sequence at the same
- * time.
- * @match: Power sequencer matching callback.
- * @targets: List of targets exposed by this sequencer.
- * @units: List of all units supported by this sequencer.
- */
- struct pwrseq_device {
- struct device dev;
- int id;
- struct module *owner;
- struct rw_semaphore rw_lock;
- struct mutex state_lock;
- pwrseq_match_func match;
- struct list_head targets;
- struct list_head units;
- };
- static struct pwrseq_device *to_pwrseq_device(struct device *dev)
- {
- return container_of(dev, struct pwrseq_device, dev);
- }
- static struct pwrseq_device *pwrseq_device_get(struct pwrseq_device *pwrseq)
- {
- get_device(&pwrseq->dev);
- return pwrseq;
- }
- static void pwrseq_device_put(struct pwrseq_device *pwrseq)
- {
- put_device(&pwrseq->dev);
- }
- /**
- * struct pwrseq_desc - Wraps access to the pwrseq_device and ensures that one
- * user cannot break the reference counting for others.
- * @pwrseq: Reference to the power sequencing device.
- * @target: Reference to the target this descriptor allows to control.
- * @powered_on: Power state set by the holder of the descriptor (not necessarily
- * corresponding to the actual power state of the device).
- */
- struct pwrseq_desc {
- struct pwrseq_device *pwrseq;
- struct pwrseq_target *target;
- bool powered_on;
- };
- static const struct bus_type pwrseq_bus = {
- .name = "pwrseq",
- };
- static void pwrseq_release(struct device *dev)
- {
- struct pwrseq_device *pwrseq = to_pwrseq_device(dev);
- struct pwrseq_target *target, *pos;
- list_for_each_entry_safe(target, pos, &pwrseq->targets, list) {
- list_del(&target->list);
- pwrseq_target_free(target);
- }
- mutex_destroy(&pwrseq->state_lock);
- ida_free(&pwrseq_ida, pwrseq->id);
- kfree(pwrseq);
- }
- static const struct device_type pwrseq_device_type = {
- .name = "power_sequencer",
- .release = pwrseq_release,
- };
- static int pwrseq_check_unit_deps(const struct pwrseq_unit_data *data,
- struct radix_tree_root *visited_units)
- {
- const struct pwrseq_unit_data *tmp, **cur;
- int ret;
- ret = radix_tree_insert(visited_units, (unsigned long)data,
- (void *)data);
- if (ret)
- return ret;
- for (cur = data->deps; cur && *cur; cur++) {
- tmp = radix_tree_lookup(visited_units, (unsigned long)*cur);
- if (tmp) {
- WARN(1, "Circular dependency in power sequencing flow detected!\n");
- return -EINVAL;
- }
- ret = pwrseq_check_unit_deps(*cur, visited_units);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int pwrseq_check_target_deps(const struct pwrseq_target_data *data)
- {
- struct radix_tree_root visited_units;
- struct radix_tree_iter iter;
- void __rcu **slot;
- int ret;
- if (!data->unit)
- return -EINVAL;
- INIT_RADIX_TREE(&visited_units, GFP_KERNEL);
- ret = pwrseq_check_unit_deps(data->unit, &visited_units);
- radix_tree_for_each_slot(slot, &visited_units, &iter, 0)
- radix_tree_delete(&visited_units, iter.index);
- return ret;
- }
- static int 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);
- static struct pwrseq_unit *
- pwrseq_unit_setup(const struct pwrseq_unit_data *data,
- struct list_head *unit_list,
- struct radix_tree_root *processed_units)
- {
- struct pwrseq_unit *unit;
- int ret;
- unit = radix_tree_lookup(processed_units, (unsigned long)data);
- if (unit)
- return pwrseq_unit_get(unit);
- unit = pwrseq_unit_new(data);
- if (!unit)
- return ERR_PTR(-ENOMEM);
- if (data->deps) {
- ret = pwrseq_unit_setup_deps(data->deps, &unit->deps,
- unit_list, processed_units);
- if (ret) {
- pwrseq_unit_put(unit);
- return ERR_PTR(ret);
- }
- }
- ret = radix_tree_insert(processed_units, (unsigned long)data, unit);
- if (ret) {
- pwrseq_unit_put(unit);
- return ERR_PTR(ret);
- }
- list_add_tail(&unit->list, unit_list);
- return unit;
- }
- static int 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)
- {
- const struct pwrseq_unit_data *pos;
- struct pwrseq_unit_dep *dep;
- struct pwrseq_unit *unit;
- int i;
- for (i = 0; data[i]; i++) {
- pos = data[i];
- unit = pwrseq_unit_setup(pos, unit_list, processed_units);
- if (IS_ERR(unit))
- return PTR_ERR(unit);
- dep = pwrseq_unit_dep_new(unit);
- if (!dep) {
- pwrseq_unit_put(unit);
- return -ENOMEM;
- }
- list_add_tail(&dep->list, dep_list);
- }
- return 0;
- }
- static int pwrseq_do_setup_targets(const struct pwrseq_target_data **data,
- struct pwrseq_device *pwrseq,
- struct radix_tree_root *processed_units)
- {
- const struct pwrseq_target_data *pos;
- struct pwrseq_target *target;
- int ret, i;
- for (i = 0; data[i]; i++) {
- pos = data[i];
- ret = pwrseq_check_target_deps(pos);
- if (ret)
- return ret;
- target = pwrseq_target_new(pos);
- if (!target)
- return -ENOMEM;
- target->unit = pwrseq_unit_setup(pos->unit, &pwrseq->units,
- processed_units);
- if (IS_ERR(target->unit)) {
- ret = PTR_ERR(target->unit);
- pwrseq_target_free(target);
- return ret;
- }
- list_add_tail(&target->list, &pwrseq->targets);
- }
- return 0;
- }
- static int pwrseq_setup_targets(const struct pwrseq_target_data **targets,
- struct pwrseq_device *pwrseq)
- {
- struct radix_tree_root processed_units;
- struct radix_tree_iter iter;
- void __rcu **slot;
- int ret;
- INIT_RADIX_TREE(&processed_units, GFP_KERNEL);
- ret = pwrseq_do_setup_targets(targets, pwrseq, &processed_units);
- radix_tree_for_each_slot(slot, &processed_units, &iter, 0)
- radix_tree_delete(&processed_units, iter.index);
- return ret;
- }
- /**
- * pwrseq_device_register() - Register a new power sequencer.
- * @config: Configuration of the new power sequencing device.
- *
- * The config structure is only used during the call and can be freed after
- * the function returns. The config structure *must* have the parent device
- * as well as the match() callback and at least one target set.
- *
- * Returns:
- * Returns the address of the new pwrseq device or ERR_PTR() on failure.
- */
- struct pwrseq_device *
- pwrseq_device_register(const struct pwrseq_config *config)
- {
- struct pwrseq_device *pwrseq;
- int ret, id;
- if (!config->parent || !config->match || !config->targets ||
- !config->targets[0])
- return ERR_PTR(-EINVAL);
- pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
- if (!pwrseq)
- return ERR_PTR(-ENOMEM);
- pwrseq->dev.type = &pwrseq_device_type;
- pwrseq->dev.bus = &pwrseq_bus;
- pwrseq->dev.parent = config->parent;
- device_set_node(&pwrseq->dev, dev_fwnode(config->parent));
- dev_set_drvdata(&pwrseq->dev, config->drvdata);
- id = ida_alloc(&pwrseq_ida, GFP_KERNEL);
- if (id < 0) {
- kfree(pwrseq);
- return ERR_PTR(id);
- }
- pwrseq->id = id;
- /*
- * From this point onwards the device's release() callback is
- * responsible for freeing resources.
- */
- device_initialize(&pwrseq->dev);
- ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id);
- if (ret)
- goto err_put_pwrseq;
- pwrseq->owner = config->owner ?: THIS_MODULE;
- pwrseq->match = config->match;
- init_rwsem(&pwrseq->rw_lock);
- mutex_init(&pwrseq->state_lock);
- INIT_LIST_HEAD(&pwrseq->targets);
- INIT_LIST_HEAD(&pwrseq->units);
- ret = pwrseq_setup_targets(config->targets, pwrseq);
- if (ret)
- goto err_put_pwrseq;
- scoped_guard(rwsem_write, &pwrseq_sem) {
- ret = device_add(&pwrseq->dev);
- if (ret)
- goto err_put_pwrseq;
- }
- return pwrseq;
- err_put_pwrseq:
- pwrseq_device_put(pwrseq);
- return ERR_PTR(ret);
- }
- EXPORT_SYMBOL_GPL(pwrseq_device_register);
- /**
- * pwrseq_device_unregister() - Unregister the power sequencer.
- * @pwrseq: Power sequencer to unregister.
- */
- void pwrseq_device_unregister(struct pwrseq_device *pwrseq)
- {
- struct device *dev = &pwrseq->dev;
- struct pwrseq_target *target;
- scoped_guard(mutex, &pwrseq->state_lock) {
- guard(rwsem_write)(&pwrseq->rw_lock);
- list_for_each_entry(target, &pwrseq->targets, list)
- WARN(target->unit->enable_count,
- "REMOVING POWER SEQUENCER WITH ACTIVE USERS\n");
- guard(rwsem_write)(&pwrseq_sem);
- device_del(dev);
- }
- pwrseq_device_put(pwrseq);
- }
- EXPORT_SYMBOL_GPL(pwrseq_device_unregister);
- static void devm_pwrseq_device_unregister(void *data)
- {
- struct pwrseq_device *pwrseq = data;
- pwrseq_device_unregister(pwrseq);
- }
- /**
- * devm_pwrseq_device_register() - Managed variant of pwrseq_device_register().
- * @dev: Managing device.
- * @config: Configuration of the new power sequencing device.
- *
- * Returns:
- * Returns the address of the new pwrseq device or ERR_PTR() on failure.
- */
- struct pwrseq_device *
- devm_pwrseq_device_register(struct device *dev,
- const struct pwrseq_config *config)
- {
- struct pwrseq_device *pwrseq;
- int ret;
- pwrseq = pwrseq_device_register(config);
- if (IS_ERR(pwrseq))
- return pwrseq;
- ret = devm_add_action_or_reset(dev, devm_pwrseq_device_unregister,
- pwrseq);
- if (ret)
- return ERR_PTR(ret);
- return pwrseq;
- }
- EXPORT_SYMBOL_GPL(devm_pwrseq_device_register);
- /**
- * pwrseq_device_get_drvdata() - Get the driver private data associated with
- * this sequencer.
- * @pwrseq: Power sequencer object.
- *
- * Returns:
- * Address of the private driver data.
- */
- void *pwrseq_device_get_drvdata(struct pwrseq_device *pwrseq)
- {
- return dev_get_drvdata(&pwrseq->dev);
- }
- EXPORT_SYMBOL_GPL(pwrseq_device_get_drvdata);
- struct pwrseq_match_data {
- struct pwrseq_desc *desc;
- struct device *dev;
- const char *target;
- };
- static int pwrseq_match_device(struct device *pwrseq_dev, void *data)
- {
- struct pwrseq_device *pwrseq = to_pwrseq_device(pwrseq_dev);
- struct pwrseq_match_data *match_data = data;
- struct pwrseq_target *target;
- int ret;
- lockdep_assert_held_read(&pwrseq_sem);
- guard(rwsem_read)(&pwrseq->rw_lock);
- if (!device_is_registered(&pwrseq->dev))
- return 0;
- ret = pwrseq->match(pwrseq, match_data->dev);
- if (ret <= 0)
- return ret;
- /* We got the matching device, let's find the right target. */
- list_for_each_entry(target, &pwrseq->targets, list) {
- if (strcmp(target->name, match_data->target))
- continue;
- match_data->desc->target = target;
- }
- /*
- * This device does not have this target. No point in deferring as it
- * will not get a new target dynamically later.
- */
- if (!match_data->desc->target)
- return -ENOENT;
- if (!try_module_get(pwrseq->owner))
- return -EPROBE_DEFER;
- match_data->desc->pwrseq = pwrseq_device_get(pwrseq);
- return 1;
- }
- /**
- * pwrseq_get() - Get the power sequencer associated with this device.
- * @dev: Device for which to get the sequencer.
- * @target: Name of the target exposed by the sequencer this device wants to
- * reach.
- *
- * Returns:
- * New power sequencer descriptor for use by the consumer driver or ERR_PTR()
- * on failure.
- */
- struct pwrseq_desc *pwrseq_get(struct device *dev, const char *target)
- {
- struct pwrseq_match_data match_data;
- int ret;
- struct pwrseq_desc *desc __free(kfree) = kzalloc(sizeof(*desc),
- GFP_KERNEL);
- if (!desc)
- return ERR_PTR(-ENOMEM);
- match_data.desc = desc;
- match_data.dev = dev;
- match_data.target = target;
- guard(rwsem_read)(&pwrseq_sem);
- ret = bus_for_each_dev(&pwrseq_bus, NULL, &match_data,
- pwrseq_match_device);
- if (ret < 0)
- return ERR_PTR(ret);
- if (ret == 0)
- /* No device matched. */
- return ERR_PTR(-EPROBE_DEFER);
- return_ptr(desc);
- }
- EXPORT_SYMBOL_GPL(pwrseq_get);
- /**
- * pwrseq_put() - Release the power sequencer descriptor.
- * @desc: Descriptor to release.
- */
- void pwrseq_put(struct pwrseq_desc *desc)
- {
- struct pwrseq_device *pwrseq;
- if (!desc)
- return;
- pwrseq = desc->pwrseq;
- if (desc->powered_on)
- pwrseq_power_off(desc);
- kfree(desc);
- module_put(pwrseq->owner);
- pwrseq_device_put(pwrseq);
- }
- EXPORT_SYMBOL_GPL(pwrseq_put);
- static void devm_pwrseq_put(void *data)
- {
- struct pwrseq_desc *desc = data;
- pwrseq_put(desc);
- }
- /**
- * devm_pwrseq_get() - Managed variant of pwrseq_get().
- * @dev: Device for which to get the sequencer and which also manages its
- * lifetime.
- * @target: Name of the target exposed by the sequencer this device wants to
- * reach.
- *
- * Returns:
- * New power sequencer descriptor for use by the consumer driver or ERR_PTR()
- * on failure.
- */
- struct pwrseq_desc *devm_pwrseq_get(struct device *dev, const char *target)
- {
- struct pwrseq_desc *desc;
- int ret;
- desc = pwrseq_get(dev, target);
- if (IS_ERR(desc))
- return desc;
- ret = devm_add_action_or_reset(dev, devm_pwrseq_put, desc);
- if (ret)
- return ERR_PTR(ret);
- return desc;
- }
- EXPORT_SYMBOL_GPL(devm_pwrseq_get);
- static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,
- struct pwrseq_unit *target);
- static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,
- struct pwrseq_unit *target);
- static int pwrseq_unit_enable_deps(struct pwrseq_device *pwrseq,
- struct list_head *list)
- {
- struct pwrseq_unit_dep *pos;
- int ret = 0;
- list_for_each_entry(pos, list, list) {
- ret = pwrseq_unit_enable(pwrseq, pos->unit);
- if (ret) {
- list_for_each_entry_continue_reverse(pos, list, list)
- pwrseq_unit_disable(pwrseq, pos->unit);
- break;
- }
- }
- return ret;
- }
- static int pwrseq_unit_disable_deps(struct pwrseq_device *pwrseq,
- struct list_head *list)
- {
- struct pwrseq_unit_dep *pos;
- int ret = 0;
- list_for_each_entry_reverse(pos, list, list) {
- ret = pwrseq_unit_disable(pwrseq, pos->unit);
- if (ret) {
- list_for_each_entry_continue(pos, list, list)
- pwrseq_unit_enable(pwrseq, pos->unit);
- break;
- }
- }
- return ret;
- }
- static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,
- struct pwrseq_unit *unit)
- {
- int ret;
- lockdep_assert_held_read(&pwrseq->rw_lock);
- lockdep_assert_held(&pwrseq->state_lock);
- if (unit->enable_count != 0) {
- unit->enable_count++;
- return 0;
- }
- ret = pwrseq_unit_enable_deps(pwrseq, &unit->deps);
- if (ret) {
- dev_err(&pwrseq->dev,
- "Failed to enable dependencies before power-on for target '%s': %d\n",
- unit->name, ret);
- return ret;
- }
- if (unit->enable) {
- ret = unit->enable(pwrseq);
- if (ret) {
- dev_err(&pwrseq->dev,
- "Failed to enable target '%s': %d\n",
- unit->name, ret);
- pwrseq_unit_disable_deps(pwrseq, &unit->deps);
- return ret;
- }
- }
- unit->enable_count++;
- return 0;
- }
- static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,
- struct pwrseq_unit *unit)
- {
- int ret;
- lockdep_assert_held_read(&pwrseq->rw_lock);
- lockdep_assert_held(&pwrseq->state_lock);
- if (unit->enable_count == 0) {
- WARN(1, "Unmatched power-off for target '%s'\n",
- unit->name);
- return -EBUSY;
- }
- if (unit->enable_count != 1) {
- unit->enable_count--;
- return 0;
- }
- if (unit->disable) {
- ret = unit->disable(pwrseq);
- if (ret) {
- dev_err(&pwrseq->dev,
- "Failed to disable target '%s': %d\n",
- unit->name, ret);
- return ret;
- }
- }
- ret = pwrseq_unit_disable_deps(pwrseq, &unit->deps);
- if (ret) {
- dev_err(&pwrseq->dev,
- "Failed to disable dependencies after power-off for target '%s': %d\n",
- unit->name, ret);
- if (unit->enable)
- unit->enable(pwrseq);
- return ret;
- }
- unit->enable_count--;
- return 0;
- }
- /**
- * pwrseq_power_on() - Issue a power-on request on behalf of the consumer
- * device.
- * @desc: Descriptor referencing the power sequencer.
- *
- * This function tells the power sequencer that the consumer wants to be
- * powered-up. The sequencer may already have powered-up the device in which
- * case the function returns 0. If the power-up sequence is already in
- * progress, the function will block until it's done and return 0. If this is
- * the first request, the device will be powered up.
- *
- * Returns:
- * 0 on success, negative error number on failure.
- */
- int pwrseq_power_on(struct pwrseq_desc *desc)
- {
- struct pwrseq_device *pwrseq;
- struct pwrseq_target *target;
- struct pwrseq_unit *unit;
- int ret;
- might_sleep();
- if (!desc || desc->powered_on)
- return 0;
- pwrseq = desc->pwrseq;
- target = desc->target;
- unit = target->unit;
- guard(rwsem_read)(&pwrseq->rw_lock);
- if (!device_is_registered(&pwrseq->dev))
- return -ENODEV;
- scoped_guard(mutex, &pwrseq->state_lock) {
- ret = pwrseq_unit_enable(pwrseq, unit);
- if (!ret)
- desc->powered_on = true;
- }
- if (target->post_enable) {
- ret = target->post_enable(pwrseq);
- if (ret) {
- pwrseq_unit_disable(pwrseq, unit);
- desc->powered_on = false;
- }
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(pwrseq_power_on);
- /**
- * pwrseq_power_off() - Issue a power-off request on behalf of the consumer
- * device.
- * @desc: Descriptor referencing the power sequencer.
- *
- * This undoes the effects of pwrseq_power_on(). It issues a power-off request
- * on behalf of the consumer and when the last remaining user does so, the
- * power-down sequence will be started. If one is in progress, the function
- * will block until it's complete and then return.
- *
- * Returns:
- * 0 on success, negative error number on failure.
- */
- int pwrseq_power_off(struct pwrseq_desc *desc)
- {
- struct pwrseq_device *pwrseq;
- struct pwrseq_unit *unit;
- int ret;
- might_sleep();
- if (!desc || !desc->powered_on)
- return 0;
- pwrseq = desc->pwrseq;
- unit = desc->target->unit;
- guard(rwsem_read)(&pwrseq->rw_lock);
- if (!device_is_registered(&pwrseq->dev))
- return -ENODEV;
- guard(mutex)(&pwrseq->state_lock);
- ret = pwrseq_unit_disable(pwrseq, unit);
- if (!ret)
- desc->powered_on = false;
- return ret;
- }
- EXPORT_SYMBOL_GPL(pwrseq_power_off);
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- struct pwrseq_debugfs_count_ctx {
- struct device *dev;
- loff_t index;
- };
- static int pwrseq_debugfs_seq_count(struct device *dev, void *data)
- {
- struct pwrseq_debugfs_count_ctx *ctx = data;
- ctx->dev = dev;
- return ctx->index-- ? 0 : 1;
- }
- static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos)
- {
- struct pwrseq_debugfs_count_ctx ctx;
- ctx.dev = NULL;
- ctx.index = *pos;
- /*
- * We're holding the lock for the entire printout so no need to fiddle
- * with device reference count.
- */
- down_read(&pwrseq_sem);
- bus_for_each_dev(&pwrseq_bus, NULL, &ctx, pwrseq_debugfs_seq_count);
- if (!ctx.index)
- return NULL;
- return ctx.dev;
- }
- static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data,
- loff_t *pos)
- {
- struct device *curr = data;
- ++*pos;
- struct device *next __free(put_device) =
- bus_find_next_device(&pwrseq_bus, curr);
- return next;
- }
- static void pwrseq_debugfs_seq_show_target(struct seq_file *seq,
- struct pwrseq_target *target)
- {
- seq_printf(seq, " target: [%s] (target unit: [%s])\n",
- target->name, target->unit->name);
- }
- static void pwrseq_debugfs_seq_show_unit(struct seq_file *seq,
- struct pwrseq_unit *unit)
- {
- struct pwrseq_unit_dep *ref;
- seq_printf(seq, " unit: [%s] - enable count: %u\n",
- unit->name, unit->enable_count);
- if (list_empty(&unit->deps))
- return;
- seq_puts(seq, " dependencies:\n");
- list_for_each_entry(ref, &unit->deps, list)
- seq_printf(seq, " [%s]\n", ref->unit->name);
- }
- static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data)
- {
- struct device *dev = data;
- struct pwrseq_device *pwrseq = to_pwrseq_device(dev);
- struct pwrseq_target *target;
- struct pwrseq_unit *unit;
- seq_printf(seq, "%s:\n", dev_name(dev));
- seq_puts(seq, " targets:\n");
- list_for_each_entry(target, &pwrseq->targets, list)
- pwrseq_debugfs_seq_show_target(seq, target);
- seq_puts(seq, " units:\n");
- list_for_each_entry(unit, &pwrseq->units, list)
- pwrseq_debugfs_seq_show_unit(seq, unit);
- return 0;
- }
- static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data)
- {
- up_read(&pwrseq_sem);
- }
- static const struct seq_operations pwrseq_debugfs_sops = {
- .start = pwrseq_debugfs_seq_start,
- .next = pwrseq_debugfs_seq_next,
- .show = pwrseq_debugfs_seq_show,
- .stop = pwrseq_debugfs_seq_stop,
- };
- DEFINE_SEQ_ATTRIBUTE(pwrseq_debugfs);
- static struct dentry *pwrseq_debugfs_dentry;
- #endif /* CONFIG_DEBUG_FS */
- static int __init pwrseq_init(void)
- {
- int ret;
- ret = bus_register(&pwrseq_bus);
- if (ret) {
- pr_err("Failed to register the power sequencer bus\n");
- return ret;
- }
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- pwrseq_debugfs_dentry = debugfs_create_file("pwrseq", 0444, NULL, NULL,
- &pwrseq_debugfs_fops);
- #endif /* CONFIG_DEBUG_FS */
- return 0;
- }
- subsys_initcall(pwrseq_init);
- static void __exit pwrseq_exit(void)
- {
- #if IS_ENABLED(CONFIG_DEBUG_FS)
- debugfs_remove_recursive(pwrseq_debugfs_dentry);
- #endif /* CONFIG_DEBUG_FS */
- bus_unregister(&pwrseq_bus);
- }
- module_exit(pwrseq_exit);
- MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
- MODULE_DESCRIPTION("Power Sequencing subsystem core");
- MODULE_LICENSE("GPL");
|