[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20211006035407.1147909-3-dmitry.baryshkov@linaro.org>
Date: Wed, 6 Oct 2021 06:53:54 +0300
From: Dmitry Baryshkov <dmitry.baryshkov@...aro.org>
To: Andy Gross <agross@...nel.org>,
Bjorn Andersson <bjorn.andersson@...aro.org>,
Ulf Hansson <ulf.hansson@...aro.org>,
Marcel Holtmann <marcel@...tmann.org>,
Johan Hedberg <johan.hedberg@...il.com>,
Luiz Augusto von Dentz <luiz.dentz@...il.com>,
Kalle Valo <kvalo@...eaurora.org>,
"David S. Miller" <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>,
Stanimir Varbanov <svarbanov@...sol.com>
Cc: linux-arm-msm@...r.kernel.org, linux-mmc@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-bluetooth@...r.kernel.org,
ath10k@...ts.infradead.org, linux-wireless@...r.kernel.org,
netdev@...r.kernel.org
Subject: [PATCH v1 02/15] power: add power sequencer subsystem
Basing on MMC's pwrseq support code, add separate power sequencer
subsystem. It will be used by other drivers to handle device power up
requirements.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@...aro.org>
---
Documentation/driver-api/index.rst | 1 +
Documentation/driver-api/pwrseq.rst | 77 +++++
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/pwrseq/Kconfig | 14 +
drivers/power/pwrseq/Makefile | 6 +
drivers/power/pwrseq/core.c | 448 ++++++++++++++++++++++++++++
include/linux/pwrseq/consumer.h | 82 +++++
include/linux/pwrseq/driver.h | 160 ++++++++++
9 files changed, 790 insertions(+)
create mode 100644 Documentation/driver-api/pwrseq.rst
create mode 100644 drivers/power/pwrseq/Kconfig
create mode 100644 drivers/power/pwrseq/Makefile
create mode 100644 drivers/power/pwrseq/core.c
create mode 100644 include/linux/pwrseq/consumer.h
create mode 100644 include/linux/pwrseq/driver.h
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index c57c609ad2eb..15f51c08db1f 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -94,6 +94,7 @@ available subsections can be seen below.
ptp
phy/index
pwm
+ pwrseq
pldmfw/index
rfkill
serial/index
diff --git a/Documentation/driver-api/pwrseq.rst b/Documentation/driver-api/pwrseq.rst
new file mode 100644
index 000000000000..df7cfce0f792
--- /dev/null
+++ b/Documentation/driver-api/pwrseq.rst
@@ -0,0 +1,77 @@
+.. Copyright 2021 Linaro Ltd.
+
+.. This documentation is free software; you can redistribute
+.. it and/or modify it under the terms of the GNU General Public
+.. License version 2 as published by the Free Software Foundation.
+
+====================
+Power Sequencing API
+====================
+
+:Author: Dmitry Baryshkov <dmitry.baryshkov@...aro.org>
+:Author: Ulf Hansson <ulf.hansson@...aro.org> (original MMC pwrseq)
+
+Introduction
+============
+
+This framework is designed to provide a standard kernel interface to
+handle power sequencing requirements for different devices.
+
+The intention is to provide a generic way to handle power sequencing of complex
+devices sitting on a variety of busses. First implementation comes from MMC
+SDIO/eMMC code, not generified to support other kinds of devices.
+
+Glossary
+--------
+
+The pwrseq API uses a number of terms which may not be familiar:
+
+Power sequencer
+
+ Electronic device (or part of it) that supplies power to other devices.
+ Unlike regulators (which typically handle single voltage), power sequencer
+ handles several voltage inputs. Also it does not provide an exact voltage
+ output. Instead it makes sure that the consumers (see below) are powered on
+ when required.
+
+Consumer
+
+ Electronic device which consumes power provided by a power sequencer.
+
+Consumer driver interface
+=========================
+
+This offers a similar API to the kernel clock or regulator framework. Consumer
+drivers use `get <#API-pwrseq-get>`__ and
+`put <#API-pwrseq-put>`__ operations to acquire and release
+power sequencers. Functions are provided to `power on
+<#API-pwrseq-full-power-on>`__ and `power off <#API-pwrseq-power-off>`__ the
+power sequencer.
+
+A stub version of this API is provided when the power sequencer framework is
+not in use in order to minimise the need to use ifdefs.
+
+Power sequencer driver interface
+================================
+
+Drivers for power sequencers register the sequencer within the pwrseq
+core, providing operations structures to the core.
+
+API reference
+=============
+
+Due to limitations of the kernel documentation framework and the
+existing layout of the source code the entire regulator API is
+documented here.
+
+.. kernel-doc:: include/linux/pwrseq/consumer.h
+ :internal:
+
+.. kernel-doc:: include/linux/pwrseq/driver.h
+ :internal:
+
+.. kernel-doc:: include/linux/pwrseq/fallback.h
+ :internal:
+
+.. kernel-doc:: drivers/power/pwrseq/core.c
+ :export:
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 696bf77a7042..c87cd2240a74 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
+source "drivers/power/pwrseq/Kconfig"
source "drivers/power/reset/Kconfig"
source "drivers/power/supply/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index effbf0377f32..1dbce454a8c4 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_POWER_RESET) += reset/
obj-$(CONFIG_POWER_SUPPLY) += supply/
+obj-$(CONFIG_PWRSEQ) += pwrseq/
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
new file mode 100644
index 000000000000..dab8f4d860fe
--- /dev/null
+++ b/drivers/power/pwrseq/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig PWRSEQ
+ bool "Power Sequencer drivers"
+ help
+ Provides support for special power sequencing drivers. Power
+ sequencer is an entity providing abstraced power on/power off/reset
+ operations for the connected devices. Possible usages include MMC
+ SDIO devices, some complex WiFi/BT chips, controlled USB hubs, etc.
+
+ Say Y here to enable support for such devices.
+
+if PWRSEQ
+
+endif
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
new file mode 100644
index 000000000000..108429ff6445
--- /dev/null
+++ b/drivers/power/pwrseq/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for power sequencer drivers.
+#
+
+obj-$(CONFIG_PWRSEQ) += core.o
diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
new file mode 100644
index 000000000000..d29b4b97b95c
--- /dev/null
+++ b/drivers/power/pwrseq/core.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2021 (c) Linaro Ltd.
+ * Author: Dmitry Baryshkov <dmitry.baryshkov@...aro.org>
+ *
+ * Based on phy-core.c:
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ */
+
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/pwrseq/consumer.h>
+#include <linux/pwrseq/driver.h>
+#include <linux/slab.h>
+
+#define to_pwrseq(a) (container_of((a), struct pwrseq, dev))
+
+static DEFINE_IDA(pwrseq_ida);
+static DEFINE_MUTEX(pwrseq_provider_mutex);
+static LIST_HEAD(pwrseq_provider_list);
+
+/**
+ * struct pwrseq_provider - a
+ */
+struct pwrseq_provider {
+ struct device *dev;
+ struct module *owner;
+ struct list_head list;
+ void *data;
+ struct pwrseq * (*of_xlate)(void *data, struct of_phandle_args *args);
+};
+
+/**
+ * pwrseq_put() - release the pwrseq
+ * @pwrseq: the pwrseq returned by pwrseq_get()
+ *
+ * Releases a refcount on the pwrseq instance received from pwrseq_get().
+ */
+void pwrseq_put(struct pwrseq *pwrseq)
+{
+ module_put(pwrseq->owner);
+ put_device(&pwrseq->dev);
+}
+EXPORT_SYMBOL_GPL(pwrseq_put);
+
+static struct pwrseq_provider *of_pwrseq_provider_lookup(struct device_node *node)
+{
+ struct pwrseq_provider *pwrseq_provider;
+
+ list_for_each_entry(pwrseq_provider, &pwrseq_provider_list, list) {
+ if (pwrseq_provider->dev->of_node == node)
+ return pwrseq_provider;
+ }
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static struct pwrseq *_of_pwrseq_get(struct device *dev, const char *id)
+{
+ struct pwrseq_provider *pwrseq_provider;
+ struct pwrseq *pwrseq;
+ struct of_phandle_args args;
+ char prop_name[64]; /* 64 is max size of property name */
+ int ret;
+
+ snprintf(prop_name, sizeof(prop_name), "%s-pwrseq", id);
+ ret = of_parse_phandle_with_args(dev->of_node, prop_name, "#pwrseq-cells", 0, &args);
+ if (ret == -ENOENT)
+ return NULL;
+ else if (ret < 0)
+ return ERR_PTR(ret);
+
+ mutex_lock(&pwrseq_provider_mutex);
+ pwrseq_provider = of_pwrseq_provider_lookup(args.np);
+ if (IS_ERR(pwrseq_provider) || !try_module_get(pwrseq_provider->owner)) {
+ pwrseq = ERR_PTR(-EPROBE_DEFER);
+ goto out_unlock;
+ }
+
+ if (!of_device_is_available(args.np)) {
+ dev_warn(pwrseq_provider->dev, "Requested pwrseq is disabled\n");
+ pwrseq = ERR_PTR(-ENODEV);
+ goto out_put_module;
+ }
+
+ pwrseq = pwrseq_provider->of_xlate(pwrseq_provider->data, &args);
+
+out_put_module:
+ module_put(pwrseq_provider->owner);
+
+out_unlock:
+ mutex_unlock(&pwrseq_provider_mutex);
+ of_node_put(args.np);
+
+ return pwrseq;
+}
+
+/**
+ * pwrseq_get() - lookup and obtain a reference to a pwrseq
+ * @dev: device for which to get the pwrseq
+ * @id: name of the pwrseq from device's point of view
+ *
+ * Returns the pwrseq instance, after getting a refcount to it; or
+ * NULL if there is no such pwrseq. The caller is responsible for
+ * calling pwrseq_put() to release that count.
+ */
+struct pwrseq * pwrseq_get(struct device *dev, const char *id)
+{
+ struct pwrseq *pwrseq;
+
+ pwrseq = _of_pwrseq_get(dev, id);
+ if (IS_ERR_OR_NULL(pwrseq))
+ return pwrseq;
+
+ if (!try_module_get(pwrseq->owner))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ get_device(&pwrseq->dev);
+
+ return pwrseq;
+}
+EXPORT_SYMBOL_GPL(pwrseq_get);
+
+static void devm_pwrseq_release(struct device *dev, void *res)
+{
+ struct pwrseq *pwrseq = *(struct pwrseq **)res;
+
+ pwrseq_put(pwrseq);
+}
+
+/**
+ * devm_pwrseq_get() - lookup and obtain a reference to a pwrseq
+ * @dev: device for which to get the pwrseq
+ * @id: name of the pwrseq from device's point of view
+ *
+ * Devres-managed variant of pwrseq_get().
+ * Returns the pwrseq instance, after getting a refcount to it; or
+ * NULL if there is no such pwrseq. Gets the pwrseq using pwrseq_get(), and
+ * associates it with the a device using devres. On driver detach, returned
+ * pwrseq is automatically put using pwrseq_put(), removing the need to call it
+ * manually.
+ */
+struct pwrseq * devm_pwrseq_get(struct device *dev, const char *id)
+{
+ struct pwrseq **ptr, *pwrseq;
+
+ ptr = devres_alloc(devm_pwrseq_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ pwrseq = pwrseq_get(dev, id);
+ if (!IS_ERR_OR_NULL(pwrseq)) {
+ *ptr = pwrseq;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return pwrseq;
+}
+EXPORT_SYMBOL_GPL(devm_pwrseq_get);
+
+/**
+ * pwrseq_pre_power_on() - perform pre-power on actions
+ * @pwrseq: pwrseq instance
+ *
+ * Perform pre-powering on actions, like pulling reset pin. This function
+ * should be called before device is being powered on. Typical usage would
+ * include MMC cards, where pwrseq subsystem is combined with the MMC power
+ * controls.
+ * In most cases there is no need to call it directly, use
+ * @pwrseq_full_power_on() instead.
+ */
+int pwrseq_pre_power_on(struct pwrseq *pwrseq)
+{
+ if (pwrseq && pwrseq->ops->pre_power_on)
+ return pwrseq->ops->pre_power_on(pwrseq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_pre_power_on);
+
+/**
+ * pwrseq_power_on() - power on the device
+ * @pwrseq: pwrseq instance
+ *
+ * Power on the device and perform post-power on actions, like pulling reset
+ * or enable pin. In most cases there is no need to call it directly, use
+ * @pwrseq_full_power_on() instead.
+ */
+int pwrseq_power_on(struct pwrseq *pwrseq)
+{
+ if (pwrseq && pwrseq->ops->power_on)
+ return pwrseq->ops->power_on(pwrseq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_power_on);
+
+/**
+ * pwrseq_power_off() - power off the device
+ * @pwrseq: pwrseq instance
+ *
+ * Power off the device clearly.
+ */
+void pwrseq_power_off(struct pwrseq *pwrseq)
+{
+ if (pwrseq && pwrseq->ops->power_off)
+ pwrseq->ops->power_off(pwrseq);
+}
+EXPORT_SYMBOL_GPL(pwrseq_power_off);
+
+/**
+ * pwrseq_reset() - reset powered device
+ * @pwrseq: pwrseq instance
+ *
+ * Reset the device controlled by the power sequencer.
+ */
+void pwrseq_reset(struct pwrseq *pwrseq)
+{
+ if (pwrseq && pwrseq->ops->reset)
+ pwrseq->ops->reset(pwrseq);
+}
+EXPORT_SYMBOL_GPL(pwrseq_reset);
+
+static void pwrseq_dev_release(struct device *dev)
+{
+ struct pwrseq *pwrseq = to_pwrseq(dev);
+
+ ida_free(&pwrseq_ida, pwrseq->id);
+ of_node_put(dev->of_node);
+ kfree(pwrseq);
+}
+
+static struct class pwrseq_class = {
+ .name = "pwrseq",
+ .dev_release = pwrseq_dev_release,
+};
+
+/**
+ * __pwrseq_create() - internal helper for pwrseq_create
+ * @dev: parent device
+ * @owner: the module providing callbacks.
+ * @ops: pwrseq device callbacks
+ *
+ * This is an internal helper for pwrseq_create which should not be called
+ * directly.
+ *
+ * Return: created instance or the wrapped error code.
+ */
+struct pwrseq *__pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops)
+{
+ struct pwrseq *pwrseq;
+ int ret;
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
+ if (!pwrseq)
+ return ERR_PTR(-ENOMEM);
+
+ ret = ida_alloc(&pwrseq_ida, GFP_KERNEL);
+ if (ret < 0)
+ goto free_pwrseq;
+
+ pwrseq->id = ret;
+
+ device_initialize(&pwrseq->dev);
+
+ pwrseq->dev.class = &pwrseq_class;
+ pwrseq->dev.parent = dev;
+ pwrseq->dev.of_node = of_node_get(dev->of_node);
+ pwrseq->ops = ops;
+ pwrseq->owner = owner;
+
+ ret = dev_set_name(&pwrseq->dev, "pwrseq-%s.%u", dev_name(dev), pwrseq->id);
+ if (ret)
+ goto put_dev;
+
+ ret = device_add(&pwrseq->dev);
+ if (ret)
+ goto put_dev;
+
+ return pwrseq;
+
+put_dev:
+ /* will call pwrseq_dev_release() to free resources */
+ put_device(&pwrseq->dev);
+
+ return ERR_PTR(ret);
+
+free_pwrseq:
+ kfree(pwrseq);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(__pwrseq_create);
+
+void pwrseq_destroy(struct pwrseq *pwrseq)
+{
+ device_unregister(&pwrseq->dev);
+}
+EXPORT_SYMBOL_GPL(pwrseq_destroy);
+
+static void devm_pwrseq_destroy(struct device *dev, void *res)
+{
+ struct pwrseq *pwrseq = *(struct pwrseq **)res;
+
+ pwrseq_destroy(pwrseq);
+}
+
+/**
+ * __devm_pwrseq_create() - devres-managed version of __pwrseq_create
+ * @dev: parent device
+ * @owner: the module providing callbacks.
+ * @ops: pwrseq device callbacks
+ *
+ * This is the devres-managed version of __pwrseq_create(). It is an internal
+ * helper which should not be called directly.
+ *
+ * Return: created instance or the wrapped error code.
+ */
+struct pwrseq *__devm_pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops)
+{
+ struct pwrseq **ptr, *pwrseq;
+
+ ptr = devres_alloc(devm_pwrseq_destroy, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ pwrseq = __pwrseq_create(dev, owner, ops);
+ if (!IS_ERR(pwrseq)) {
+ *ptr = pwrseq;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return pwrseq;
+}
+EXPORT_SYMBOL_GPL(__devm_pwrseq_create);
+
+/**
+ * __of_pwrseq_provider_register - internal version of of_pwrseq_provider_register
+ * @dev: handling device
+ * @owner: the module providing callbacks.
+ * @xlate: xlate function returning pwrseq instance corresponding to OF args
+ * @data: provider-specific data to be passed to xlate function
+ *
+ * This is an internal helper of of_pwrseq_provider_register, it should not be
+ * called directly.
+ */
+struct pwrseq_provider *__of_pwrseq_provider_register(struct device *dev,
+ struct module *owner,
+ struct pwrseq * (*of_xlate)(void *data,
+ struct of_phandle_args *args),
+ void *data)
+{
+ struct pwrseq_provider *pwrseq_provider;
+
+ pwrseq_provider = kzalloc(sizeof(*pwrseq_provider), GFP_KERNEL);
+ if (!pwrseq_provider)
+ return ERR_PTR(-ENOMEM);
+
+ if (!fwnode_property_present(dev->fwnode, "#pwrseq-cells"))
+ dev_warn(dev, "no #pwrseq-cells property found, please add the property to the provider\n");
+
+ pwrseq_provider->dev = dev;
+ pwrseq_provider->owner = owner;
+ pwrseq_provider->of_xlate = of_xlate;
+ pwrseq_provider->data = data;
+
+ mutex_lock(&pwrseq_provider_mutex);
+ list_add_tail(&pwrseq_provider->list, &pwrseq_provider_list);
+ mutex_unlock(&pwrseq_provider_mutex);
+
+ return pwrseq_provider;
+}
+EXPORT_SYMBOL_GPL(__of_pwrseq_provider_register);
+
+/**
+ * of_pwrseq_provider_unregister() - unregister pwrseq provider
+ * @pwrseq_provider: pwrseq provider to unregister
+ *
+ * Unregister pwrseq provider previously registered by of_pwrseq_provider_register().
+ */
+void of_pwrseq_provider_unregister(struct pwrseq_provider *pwrseq_provider)
+{
+ if (IS_ERR(pwrseq_provider))
+ return;
+
+ mutex_lock(&pwrseq_provider_mutex);
+ list_del(&pwrseq_provider->list);
+ kfree(pwrseq_provider);
+ mutex_unlock(&pwrseq_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_provider_unregister);
+
+static void devm_pwrseq_provider_unregister(struct device *dev, void *res)
+{
+ struct pwrseq_provider *pwrseq_provider = *(struct pwrseq_provider **)res;
+
+ of_pwrseq_provider_unregister(pwrseq_provider);
+}
+
+/**
+ * __devm_of_pwrseq_provider_register - internal version of devm_of_pwrseq_provider_register
+ * @dev: handling device
+ * @owner: the module providing callbacks.
+ * @xlate: xlate function returning pwrseq instance corresponding to OF args
+ * @data: provider-specific data to be passed to xlate function
+ *
+ * This is an internal helper of devm_of_pwrseq_provider_register, it should
+ * not be called directly.
+ */
+struct pwrseq_provider *__devm_of_pwrseq_provider_register(struct device *dev,
+ struct module *owner,
+ struct pwrseq * (*of_xlate)(void *data,
+ struct of_phandle_args *args),
+ void *data)
+{
+ struct pwrseq_provider **ptr, *pwrseq_provider;
+
+ ptr = devres_alloc(devm_pwrseq_provider_unregister, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ pwrseq_provider = __of_pwrseq_provider_register(dev, owner, of_xlate, data);
+ if (!IS_ERR(pwrseq_provider)) {
+ *ptr = pwrseq_provider;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return pwrseq_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_pwrseq_provider_register);
+
+static int __init pwrseq_core_init(void)
+{
+ return class_register(&pwrseq_class);
+}
+device_initcall(pwrseq_core_init);
diff --git a/include/linux/pwrseq/consumer.h b/include/linux/pwrseq/consumer.h
new file mode 100644
index 000000000000..7c8efefd3184
--- /dev/null
+++ b/include/linux/pwrseq/consumer.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2021 Linaro Ltd.
+ */
+
+#ifndef __LINUX_PWRSEQ_CONSUMER_H__
+#define __LINUX_PWRSEQ_CONSUMER_H__
+
+struct pwrseq;
+struct device;
+
+#if defined(CONFIG_PWRSEQ)
+
+struct pwrseq *__must_check pwrseq_get(struct device *dev, const char *id);
+struct pwrseq *__must_check devm_pwrseq_get(struct device *dev, const char *id);
+
+void pwrseq_put(struct pwrseq *pwrseq);
+
+int pwrseq_pre_power_on(struct pwrseq *pwrseq);
+int pwrseq_power_on(struct pwrseq *pwrseq);
+void pwrseq_power_off(struct pwrseq *pwrseq);
+void pwrseq_reset(struct pwrseq *pwrseq);
+
+#else
+
+static inline struct pwrseq *__must_check
+pwrseq_get(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
+static inline struct pwrseq *__must_check
+devm_pwrseq_get(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
+static inline void pwrseq_put(struct pwrseq *pwrseq)
+{
+}
+
+static inline int pwrseq_pre_power_on(struct pwrseq *pwrseq)
+{
+ return -ENOSYS;
+}
+
+static inline int pwrseq_power_on(struct pwrseq *pwrseq)
+{
+ return -ENOSYS;
+}
+
+static inline void pwrseq_power_off(struct pwrseq *pwrseq)
+{
+}
+
+static inline void pwrseq_reset(struct pwrseq *pwrseq)
+{
+}
+
+#endif
+
+/**
+ * pwrseq_full_power_on() - Perform full power on of the sequencer
+ * @pwrseq: the power sequencer to power on
+ *
+ * Perform full power on of the sequencer, including pre power on and
+ * power on steps.
+ *
+ * Return: 0 upon no error; -error upon error.
+ */
+static inline int pwrseq_full_power_on(struct pwrseq *pwrseq)
+{
+ int ret;
+
+ ret = pwrseq_pre_power_on(pwrseq);
+ if (ret)
+ return ret;
+
+ return pwrseq_power_on(pwrseq);
+}
+
+#endif /* __LINUX_PWRSEQ_CONSUMER_H__ */
diff --git a/include/linux/pwrseq/driver.h b/include/linux/pwrseq/driver.h
new file mode 100644
index 000000000000..bdb8a25a8504
--- /dev/null
+++ b/include/linux/pwrseq/driver.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2021 Linaro Ltd.
+ */
+
+#ifndef __LINUX_PWRSEQ_DRIVER_H__
+#define __LINUX_PWRSEQ_DRIVER_H__
+
+#include <linux/device.h>
+
+struct pwrseq;
+
+/**
+ * struct pwrseq_ops - power sequencer operations.
+ *
+ * @pre_power_on: Perform pre powering operations (like ensuring that the
+ * device will be held in the reset)
+ * @power_on: Power on the sequencer, making sure that the consumer
+ * devices can be operated
+ * @power_off: Power off the sequencer, removing power from the comsumer
+ * device (if possible)
+ * @reset: Reset the consumer device
+ */
+struct pwrseq_ops {
+ int (*pre_power_on)(struct pwrseq *pwrseq);
+ int (*power_on)(struct pwrseq *pwrseq);
+ void (*power_off)(struct pwrseq *pwrseq);
+ void (*reset)(struct pwrseq *pwrseq);
+};
+
+struct module;
+
+/**
+ * struct pwrseq - private pwrseq data
+ *
+ * Power sequencer device, one for each power sequencer.
+ *
+ * This should *not* be used directly by anything except the pwrseq core.
+ */
+struct pwrseq {
+ struct device dev;
+ const struct pwrseq_ops *ops;
+ unsigned int id;
+ struct module *owner;
+};
+
+struct pwrseq *__pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops);
+struct pwrseq *__devm_pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops);
+
+/**
+ * pwrseq_create() - create pwrseq instance
+ * @dev: parent device
+ * @ops: pwrseq device callbacks
+ *
+ * Create new pwrseq instance parenting with @dev using provided @ops set of
+ * callbacks. Create instance should be destroyed using @pwrseq_destroy().
+ *
+ * Return: created instance or the wrapped error code.
+ */
+#define pwrseq_create(dev, ops, data) __pwrseq_create((dev), THIS_MODULE, (ops))
+
+/**
+ * devm_pwrseq_create() - devres-managed version of pwrseq_create
+ * @dev: parent device
+ * @ops: pwrseq device callbacks
+ *
+ * This is the devres-managed version of pwrseq_create(). It creates new
+ * pwrseq instance parenting with @dev using provided @ops set of
+ * callbacks. Returned object is destroyed automatically, one must not call
+ * pwrseq_destroy().
+ *
+ * Return: created instance or the wrapped error code.
+ */
+#define devm_pwrseq_create(dev, ops, data) __devm_pwrseq_create((dev), THIS_MODULE, (ops))
+
+void pwrseq_destroy(struct pwrseq *pwrseq);
+
+/**
+ * pwrseq_set_data() - get drv-specific data for the pwrseq
+ * @pwrseq: the pwrseq to get driver data for
+ * @data: the data to set
+ *
+ * Sets the driver-specific data for the provided powrseq instance.
+ */
+static inline void pwrseq_set_drvdata(struct pwrseq *pwrseq, void *data)
+{
+ dev_set_drvdata(&pwrseq->dev, data);
+}
+
+/**
+ * pwrseq_get_data() - get drv-specific data for the pwrseq
+ * @pwrseq: the pwrseq to get driver data for
+ *
+ * Returns driver-specific data for the provided powrseq instance.
+ */
+static inline void *pwrseq_get_drvdata(struct pwrseq *pwrseq)
+{
+ return dev_get_drvdata(&pwrseq->dev);
+}
+
+/**
+ * of_pwrseq_provider_register() - register OF pwrseq provider
+ * @dev: handling device
+ * @xlate: xlate function returning pwrseq instance corresponding to OF args
+ * @data: provider-specific data to be passed to xlate function
+ *
+ * This macros registers OF-specific pwrseq provider. Pwrseq core will call
+ * specified @xlate function to retrieve pwrseq instance corresponding to
+ * device tree arguments. Returned pwrseq provider should be unregistered using
+ * of_pwrseq_provider_unregister().
+ */
+#define of_pwrseq_provider_register(dev, xlate, data) \
+ __of_pwrseq_provider_register((dev), THIS_MODULE, (xlate), (data))
+
+/**
+ * devm_of_pwrseq_provider_register() - devres-managed version of of_pwrseq_provider_register
+ * @dev: handling device
+ * @xlate: xlate function returning pwrseq instance corresponding to OF args
+ * @data: provider-specific data to be passed to xlate function
+ *
+ * This is a devres-managed version of of_pwrseq_provider_register().
+ * This macros registers OF-specific pwrseq provider. Pwrseq core will call
+ * specified @xlate function to retrieve pwrseq instance corresponding to
+ * device tree arguments. Returned pwrseq provider is automatically
+ * unregistered, without the need to call of_pwrseq_provider_unregister().
+ */
+#define devm_of_pwrseq_provider_register(dev, xlate, data) \
+ __devm_of_pwrseq_provider_register((dev), THIS_MODULE, (xlate), (data))
+
+struct of_phandle_args;
+
+struct pwrseq_provider *__of_pwrseq_provider_register(struct device *dev,
+ struct module *owner,
+ struct pwrseq * (*of_xlate)(void *data,
+ struct of_phandle_args *args),
+ void *data);
+struct pwrseq_provider *__devm_of_pwrseq_provider_register(struct device *dev,
+ struct module *owner,
+ struct pwrseq * (*of_xlate)(void *data,
+ struct of_phandle_args *args),
+ void *data);
+void of_pwrseq_provider_unregister(struct pwrseq_provider *pwrseq_provider);
+
+/**
+ * of_pwrseq_xlate_single() - returns the pwrseq instance from pwrseq provider
+ * @data: the pwrseq provider data
+ * @args: of_phandle_args (not used here)
+ *
+ * Intended to be used by pwrseq provider for the common case where
+ * #pwrseq-cells is 0. For other cases where #pwrseq-cells is greater than '0',
+ * the pwrseq provider should provide a custom of_xlate function that reads the
+ * *args* and returns the appropriate pwrseq.
+ */
+static inline struct pwrseq *of_pwrseq_xlate_single(void *data,
+ struct of_phandle_args *args)
+{
+ return data;
+}
+
+#endif /* __LINUX_PWRSEQ_DRIVER_H__ */
--
2.33.0
Powered by blists - more mailing lists