1137 lines · 27.3 KB · C
RawDownload
// 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_obj(*unit);	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_obj(*dep);	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_obj(*target);	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_obj(*pwrseq);	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; 

Lines 1–500 of 1137.