Power sequencer support requires changing device tree. To ease migration to pwrseq, add support for pwrseq 'fallback': let the power sequencer driver register special handler that if matched will create pwrseq instance basing on the consumer device tree data. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> --- drivers/power/pwrseq/Makefile | 2 +- drivers/power/pwrseq/core.c | 3 ++ drivers/power/pwrseq/fallback.c | 75 +++++++++++++++++++++++++++++++++ include/linux/pwrseq/fallback.h | 36 ++++++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 drivers/power/pwrseq/fallback.c create mode 100644 include/linux/pwrseq/fallback.h diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile index 556bf5582d47..949ec848cf00 100644 --- a/drivers/power/pwrseq/Makefile +++ b/drivers/power/pwrseq/Makefile @@ -3,7 +3,7 @@ # Makefile for power sequencer drivers. # -obj-$(CONFIG_PWRSEQ) += core.o +obj-$(CONFIG_PWRSEQ) += core.o fallback.o obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o obj-$(CONFIG_PWRSEQ_QCA) += pwrseq_qca.o diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c index 2e4e9d123e60..a43977a0eed8 100644 --- a/drivers/power/pwrseq/core.c +++ b/drivers/power/pwrseq/core.c @@ -15,6 +15,7 @@ #include <linux/pm_runtime.h> #include <linux/pwrseq/consumer.h> #include <linux/pwrseq/driver.h> +#include <linux/pwrseq/fallback.h> #include <linux/slab.h> #define to_pwrseq(a) (container_of((a), struct pwrseq, dev)) @@ -108,6 +109,8 @@ struct pwrseq * __pwrseq_get(struct device *dev, const char *id, bool optional) struct device_link *link; pwrseq = _of_pwrseq_get(dev, id); + if (pwrseq == NULL) + pwrseq = pwrseq_fallback_get(dev, id); if (pwrseq == NULL) return optional ? NULL : ERR_PTR(-ENODEV); else if (IS_ERR(pwrseq)) diff --git a/drivers/power/pwrseq/fallback.c b/drivers/power/pwrseq/fallback.c new file mode 100644 index 000000000000..6ecf24dd8f29 --- /dev/null +++ b/drivers/power/pwrseq/fallback.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 (c) Linaro Ltd. + * Author: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/pwrseq/fallback.h> +#include <linux/slab.h> + +static DEFINE_MUTEX(pwrseq_fallback_mutex); +static LIST_HEAD(pwrseq_fallback_list); + +int __pwrseq_fallback_register(struct pwrseq_fallback *fallback, struct module *owner) +{ + if (!try_module_get(owner)) + return -EPROBE_DEFER; + + fallback->owner = owner; + + mutex_lock(&pwrseq_fallback_mutex); + list_add_tail(&fallback->list, &pwrseq_fallback_list); + mutex_unlock(&pwrseq_fallback_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(__pwrseq_fallback_register); + +void pwrseq_fallback_unregister(struct pwrseq_fallback *fallback) +{ + mutex_lock(&pwrseq_fallback_mutex); + list_del(&fallback->list); + mutex_unlock(&pwrseq_fallback_mutex); + + module_put(fallback->owner); + + kfree(fallback); +} +EXPORT_SYMBOL_GPL(pwrseq_fallback_unregister); + +static bool pwrseq_fallback_match(struct device *dev, struct pwrseq_fallback *fallback) +{ + if (of_match_device(fallback->of_match_table, dev) != NULL) + return true; + + /* We might add support for other matching options later */ + + return false; +} + +struct pwrseq *pwrseq_fallback_get(struct device *dev, const char *id) +{ + struct pwrseq_fallback *fallback; + struct pwrseq *pwrseq = ERR_PTR(-ENODEV); + + mutex_lock(&pwrseq_fallback_mutex); + + list_for_each_entry(fallback, &pwrseq_fallback_list, list) { + if (!pwrseq_fallback_match(dev, fallback)) + continue; + + pwrseq = fallback->get(dev, id); + break; + } + + mutex_unlock(&pwrseq_fallback_mutex); + + if (!IS_ERR_OR_NULL(pwrseq)) + dev_warn(dev, "legacy pwrseq support used for the device\n"); + + return pwrseq; +} diff --git a/include/linux/pwrseq/fallback.h b/include/linux/pwrseq/fallback.h new file mode 100644 index 000000000000..616049df179f --- /dev/null +++ b/include/linux/pwrseq/fallback.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Linaro Ltd. + */ + +#ifndef __LINUX_PWRSEQ_FALLBACK_H__ +#define __LINUX_PWRSEQ_FALLBACK_H__ + +#include <linux/list.h> + +struct pwrseq; + +struct device; +struct module; +struct of_device_id; + +struct pwrseq_fallback { + struct list_head list; + struct module *owner; + + const struct of_device_id *of_match_table; + + struct pwrseq *(*get)(struct device *dev, const char *id); +}; + +/* provider interface */ + +int __pwrseq_fallback_register(struct pwrseq_fallback *fallback, struct module *owner); +#define pwrseq_fallback_register(fallback) __pwrseq_fallback_register(fallback, THIS_MODULE) + +void pwrseq_fallback_unregister(struct pwrseq_fallback *fallback); + +/* internal interface */ +struct pwrseq *pwrseq_fallback_get(struct device *dev, const char *id); + +#endif /* __LINUX_PWRSEQ_DRIVER_H__ */ -- 2.33.0