linux/drivers/power/sequencing/core.c

// 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 {};

static struct pwrseq_unit *pwrseq_unit_new(const struct pwrseq_unit_data *data)
{}

static struct pwrseq_unit *pwrseq_unit_get(struct pwrseq_unit *unit)
{}

static void pwrseq_unit_release(struct kref *ref);

static void pwrseq_unit_put(struct pwrseq_unit *unit)
{}

/**
 * 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 {};

static struct pwrseq_unit_dep *pwrseq_unit_dep_new(struct pwrseq_unit *unit)
{}

static void pwrseq_unit_dep_free(struct pwrseq_unit_dep *ref)
{}

static void pwrseq_unit_free_deps(struct list_head *list)
{}

static void pwrseq_unit_release(struct kref *ref)
{}

/**
 * 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 {};

static struct pwrseq_target *
pwrseq_target_new(const struct pwrseq_target_data *data)
{}

static void pwrseq_target_free(struct pwrseq_target *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 {};

static struct pwrseq_device *to_pwrseq_device(struct device *dev)
{}

static struct pwrseq_device *pwrseq_device_get(struct pwrseq_device *pwrseq)
{}

static void pwrseq_device_put(struct pwrseq_device *pwrseq)
{}

/**
 * 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 {};

static const struct bus_type pwrseq_bus =;

static void pwrseq_release(struct device *dev)
{}

static const struct device_type pwrseq_device_type =;

static int pwrseq_check_unit_deps(const struct pwrseq_unit_data *data,
				  struct radix_tree_root *visited_units)
{}

static int pwrseq_check_target_deps(const struct pwrseq_target_data *data)
{}

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)
{}

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 int pwrseq_do_setup_targets(const struct pwrseq_target_data **data,
				   struct pwrseq_device *pwrseq,
				   struct radix_tree_root *processed_units)
{}

static int pwrseq_setup_targets(const struct pwrseq_target_data **targets,
				struct pwrseq_device *pwrseq)
{}

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

/**
 * pwrseq_device_unregister() - Unregister the power sequencer.
 * @pwrseq: Power sequencer to unregister.
 */
void pwrseq_device_unregister(struct pwrseq_device *pwrseq)
{}
EXPORT_SYMBOL_GPL();

static void devm_pwrseq_device_unregister(void *data)
{}

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

struct pwrseq_match_data {};

static int pwrseq_match_device(struct device *pwrseq_dev, void *data)
{}

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

/**
 * pwrseq_put() - Release the power sequencer descriptor.
 * @desc: Descriptor to release.
 */
void pwrseq_put(struct pwrseq_desc *desc)
{}
EXPORT_SYMBOL_GPL();

static void devm_pwrseq_put(void *data)
{}

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

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)
{}

static int pwrseq_unit_disable_deps(struct pwrseq_device *pwrseq,
				    struct list_head *list)
{}

static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,
			      struct pwrseq_unit *unit)
{}

static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,
			       struct pwrseq_unit *unit)
{}

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

/**
 * 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)
{}
EXPORT_SYMBOL_GPL();

#if IS_ENABLED(CONFIG_DEBUG_FS)

struct pwrseq_debugfs_count_ctx {};

static int pwrseq_debugfs_seq_count(struct device *dev, void *data)
{}

static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos)
{}

static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data,
				     loff_t *pos)
{}

static void pwrseq_debugfs_seq_show_target(struct seq_file *seq,
					   struct pwrseq_target *target)
{}

static void pwrseq_debugfs_seq_show_unit(struct seq_file *seq,
					 struct pwrseq_unit *unit)
{}

static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data)
{}

static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data)
{}

static const struct seq_operations pwrseq_debugfs_sops =;
DEFINE_SEQ_ATTRIBUTE();

static struct dentry *pwrseq_debugfs_dentry;

#endif /* CONFIG_DEBUG_FS */

static int __init pwrseq_init(void)
{}
subsys_initcall(pwrseq_init);

static void __exit pwrseq_exit(void)
{}
module_exit(pwrseq_exit);

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();