mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Andrey Smirnov <andrew.smirnov@gmail.com>
To: barebox@lists.infradead.org
Cc: Andrey Smirnov <andrew.smirnov@gmail.com>
Subject: [PATCH 08/21] drivers: base: Port power management code from Linux
Date: Tue,  8 Jan 2019 23:11:57 -0800	[thread overview]
Message-ID: <20190109071210.18896-9-andrew.smirnov@gmail.com> (raw)
In-Reply-To: <20190109071210.18896-1-andrew.smirnov@gmail.com>

Port an extremely abridged version of power management/power domain
code from Linux as a dependency of i.MX7D PCIe work. Currenlty only
bare minimum of functionality is implemented.

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/Kconfig         |   1 +
 drivers/base/Kconfig    |   3 +
 drivers/base/Makefile   |   4 +-
 drivers/base/platform.c |   7 ++
 drivers/base/power.c    | 245 ++++++++++++++++++++++++++++++++++++++++
 include/pm_domain.h     |  82 ++++++++++++++
 6 files changed, 341 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/Kconfig
 create mode 100644 drivers/base/power.c
 create mode 100644 include/pm_domain.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 1e0246da6..c3bf9dfe1 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -1,5 +1,6 @@
 menu "Drivers"
 
+source "drivers/base/Kconfig"
 source "drivers/efi/Kconfig"
 source "drivers/of/Kconfig"
 source "drivers/aiodev/Kconfig"
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
new file mode 100644
index 000000000..1e13e5ed9
--- /dev/null
+++ b/drivers/base/Kconfig
@@ -0,0 +1,3 @@
+
+config PM_GENERIC_DOMAINS
+	bool
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 4bd421774..6d2cef8e1 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -2,4 +2,6 @@ obj-y	+= bus.o
 obj-y	+= driver.o
 obj-y	+= platform.o
 obj-y	+= resource.o
-obj-y	+= regmap/
\ No newline at end of file
+obj-y	+= regmap/
+
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += power.o
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 85bdfb014..1d3fa2eb4 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -21,9 +21,16 @@
 #include <errno.h>
 #include <init.h>
 #include <of.h>
+#include <pm_domain.h>
 
 static int platform_probe(struct device_d *dev)
 {
+	int ret;
+
+	ret = genpd_dev_pm_attach(dev);
+	if (ret < 0)
+		return ret;
+
 	return dev->driver->probe(dev);
 }
 
diff --git a/drivers/base/power.c b/drivers/base/power.c
new file mode 100644
index 000000000..fd9c4e2ba
--- /dev/null
+++ b/drivers/base/power.c
@@ -0,0 +1,245 @@
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <of.h>
+
+#include <pm_domain.h>
+
+#define genpd_status_on(genpd)		(genpd->status == GPD_STATE_ACTIVE)
+
+static LIST_HEAD(gpd_list);
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ *
+ * Returns 0 on successful initialization, else a negative error code.
+ */
+int pm_genpd_init(struct generic_pm_domain *genpd, void *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return -EINVAL;
+
+	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
+
+	list_add(&genpd->gpd_list_node, &gpd_list);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm_genpd_init);
+
+/**
+ * struct of_genpd_provider - PM domain provider registration structure
+ * @link: Entry in global list of PM domain providers
+ * @node: Pointer to device tree node of PM domain provider
+ * @xlate: Provider-specific xlate callback mapping a set of specifier cells
+ *         into a PM domain.
+ * @data: context pointer to be passed into @xlate callback
+ */
+struct of_genpd_provider {
+	struct list_head link;
+	struct device_node *node;
+	genpd_xlate_t xlate;
+	void *data;
+};
+
+/* List of registered PM domain providers. */
+static LIST_HEAD(of_genpd_providers);
+
+static bool genpd_present(const struct generic_pm_domain *genpd)
+{
+	const struct generic_pm_domain *gpd;
+
+	if (IS_ERR_OR_NULL(genpd))
+		return false;
+
+	list_for_each_entry(gpd, &gpd_list, gpd_list_node)
+		if (gpd == genpd)
+			return true;
+
+	return false;
+}
+
+/**
+ * genpd_xlate_simple() - Xlate function for direct node-domain mapping
+ * @genpdspec: OF phandle args to map into a PM domain
+ * @data: xlate function private data - pointer to struct generic_pm_domain
+ *
+ * This is a generic xlate function that can be used to model PM domains that
+ * have their own device tree nodes. The private data of xlate function needs
+ * to be a valid pointer to struct generic_pm_domain.
+ */
+static struct generic_pm_domain *genpd_xlate_simple(
+					struct of_phandle_args *genpdspec,
+					void *data)
+{
+	return data;
+}
+
+/**
+ * genpd_add_provider() - Register a PM domain provider for a node
+ * @np: Device node pointer associated with the PM domain provider.
+ * @xlate: Callback for decoding PM domain from phandle arguments.
+ * @data: Context pointer for @xlate callback.
+ */
+static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+			      void *data)
+{
+	struct of_genpd_provider *cp;
+
+	cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+	if (!cp)
+		return -ENOMEM;
+
+	cp->node  = np;
+	cp->data  = data;
+	cp->xlate = xlate;
+
+	list_add(&cp->link, &of_genpd_providers);
+	pr_debug("Added domain provider from %pOF\n", np);
+
+	return 0;
+}
+
+/**
+ * of_genpd_add_provider_simple() - Register a simple PM domain provider
+ * @np: Device node pointer associated with the PM domain provider.
+ * @genpd: Pointer to PM domain associated with the PM domain provider.
+ */
+int of_genpd_add_provider_simple(struct device_node *np,
+				 struct generic_pm_domain *genpd)
+{
+	int ret = -EINVAL;
+
+	if (!np || !genpd)
+		return -EINVAL;
+
+	if (genpd_present(genpd))
+		ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
+
+/**
+ * genpd_get_from_provider() - Look-up PM domain
+ * @genpdspec: OF phandle args to use for look-up
+ *
+ * Looks for a PM domain provider under the node specified by @genpdspec and if
+ * found, uses xlate function of the provider to map phandle args to a PM
+ * domain.
+ *
+ * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
+ * on failure.
+ */
+static struct generic_pm_domain *genpd_get_from_provider(
+					struct of_phandle_args *genpdspec)
+{
+	struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
+	struct of_genpd_provider *provider;
+
+	if (!genpdspec)
+		return ERR_PTR(-EINVAL);
+
+	/* Check if we have such a provider in our array */
+	list_for_each_entry(provider, &of_genpd_providers, link) {
+		if (provider->node == genpdspec->np)
+			genpd = provider->xlate(genpdspec, provider->data);
+		if (!IS_ERR(genpd))
+			break;
+	}
+
+	return genpd;
+}
+
+static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
+{
+	if (!genpd->power_on)
+		return 0;
+
+	return genpd->power_on(genpd);
+}
+
+/**
+ * genpd_power_on - Restore power to a given PM domain and its masters.
+ * @genpd: PM domain to power up.
+ * @depth: nesting count for lockdep.
+ *
+ * Restore power to @genpd and all of its masters so that it is possible to
+ * resume a device belonging to it.
+ */
+static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
+{
+	int ret;
+
+	if (genpd_status_on(genpd))
+		return 0;
+
+	ret = _genpd_power_on(genpd, true);
+	if (ret)
+		return ret;
+
+	genpd->status = GPD_STATE_ACTIVE;
+
+	return 0;
+}
+
+static int __genpd_dev_pm_attach(struct device_d *dev, struct device_node *np,
+				 unsigned int index, bool power_on)
+{
+	struct of_phandle_args pd_args;
+	struct generic_pm_domain *pd;
+	int ret;
+
+	ret = of_parse_phandle_with_args(np, "power-domains",
+				"#power-domain-cells", index, &pd_args);
+	if (ret < 0)
+		return ret;
+
+	pd = genpd_get_from_provider(&pd_args);
+	if (IS_ERR(pd)) {
+		ret = PTR_ERR(pd);
+		dev_dbg(dev, "%s() failed to find PM domain: %d\n",
+			__func__, ret);
+		return driver_deferred_probe_check_state(dev);
+	}
+
+	dev_dbg(dev, "adding to PM domain %s\n", pd->name);
+
+	if (power_on)
+		ret = genpd_power_on(pd, 0);
+
+	return ret ?: 1;
+}
+
+/**
+ * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
+ * @dev: Device to attach.
+ *
+ * Parse device's OF node to find a PM domain specifier. If such is found,
+ * attaches the device to retrieved pm_domain ops.
+ *
+ * Returns 1 on successfully attached PM domain, 0 when the device don't need a
+ * PM domain or when multiple power-domains exists for it, else a negative error
+ * code. Note that if a power-domain exists for the device, but it cannot be
+ * found or turned on, then return -EPROBE_DEFER to ensure that the device is
+ * not probed and to re-try again later.
+ */
+int genpd_dev_pm_attach(struct device_d *dev)
+{
+	if (!dev->device_node)
+		return 0;
+
+	/*
+	 * Devices with multiple PM domains must be attached separately, as we
+	 * can only attach one PM domain per device.
+	 */
+	if (of_count_phandle_with_args(dev->device_node, "power-domains",
+				       "#power-domain-cells") != 1)
+		return 0;
+
+	return __genpd_dev_pm_attach(dev, dev->device_node, 0, true);
+}
+EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
diff --git a/include/pm_domain.h b/include/pm_domain.h
new file mode 100644
index 000000000..6d59587ec
--- /dev/null
+++ b/include/pm_domain.h
@@ -0,0 +1,82 @@
+#ifndef _PM_DOMAIN_H
+#define _PM_DOMAIN_H
+
+enum gpd_status {
+	GPD_STATE_ACTIVE = 0,	/* PM domain is active */
+	GPD_STATE_POWER_OFF,	/* PM domain is off */
+};
+
+struct generic_pm_domain {
+	const char *name;
+	struct list_head gpd_list_node;	/* Node in the global PM domains list */
+
+	enum gpd_status status;	/* Current state of the domain */
+
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+};
+
+typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
+						   void *data);
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+
+int genpd_dev_pm_attach(struct device_d *dev);
+
+/**
+ * dev_pm_domain_attach - Attach a device to its PM domain.
+ * @dev: Device to attach.
+ * @power_on: Used to indicate whether we should power on the device.
+ *
+ * The @dev may only be attached to a single PM domain. By iterating through
+ * the available alternatives we try to find a valid PM domain for the device.
+ * As attachment succeeds, the ->detach() callback in the struct dev_pm_domain
+ * should be assigned by the corresponding attach function.
+ *
+ * This function should typically be invoked from subsystem level code during
+ * the probe phase. Especially for those that holds devices which requires
+ * power management through PM domains.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns 0 on successfully attached PM domain or negative error code.
+ */
+static inline int dev_pm_domain_attach(struct device_d *dev, bool power_on)
+{
+	return genpd_dev_pm_attach(dev);
+}
+
+int pm_genpd_init(struct generic_pm_domain *genpd, void *gov, bool is_off);
+
+int of_genpd_add_provider_simple(struct device_node *np,
+				 struct generic_pm_domain *genpd);
+
+#else
+
+static inline int pm_genpd_init(struct generic_pm_domain *genpd,
+				void *gov, bool is_off)
+{
+	return -ENOSYS;
+}
+
+static inline int genpd_dev_pm_attach(struct device_d *dev)
+{
+	return 0;
+}
+
+static inline int dev_pm_domain_attach(struct device_d *dev, bool power_on)
+{
+	return 0;
+}
+
+static inline int
+of_genpd_add_provider_simple(struct device_node *np,
+			     struct generic_pm_domain *genpd)
+{
+	return -ENOTSUPP;
+}
+
+#endif
+
+#endif
\ No newline at end of file
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  parent reply	other threads:[~2019-01-09  7:12 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-09  7:11 [PATCH 00/21] PCIe support for i.MX7 Andrey Smirnov
2019-01-09  7:11 ` [PATCH 01/21] regulator: Convert drivers to use struct regulator_desc Andrey Smirnov
2019-01-09  7:11 ` [PATCH 02/21] regulator: Port basic regmap regulator functions Andrey Smirnov
2019-01-09  7:11 ` [PATCH 03/21] regulator: Add support for setting regulator's voltage Andrey Smirnov
2019-01-09  7:11 ` [PATCH 04/21] base: driver: Drop redundant list_empty() check Andrey Smirnov
2019-01-09  7:11 ` [PATCH 05/21] base: Port driver_deferred_probe_check_state() from Linux Andrey Smirnov
2019-01-09  7:11 ` [PATCH 06/21] regulator: Add primitive support for deferred probe Andrey Smirnov
2019-01-09  7:11 ` [PATCH 07/21] regulator: Port ANATOP driver from Linux Andrey Smirnov
2019-01-09  7:11 ` Andrey Smirnov [this message]
2019-01-09  7:11 ` [PATCH 09/21] soc: imx: Add GPCv2 power gating driver Andrey Smirnov
2019-01-09  7:11 ` [PATCH 10/21] soc: imx: gpcv2: fix regulator deferred probe Andrey Smirnov
2019-01-10  7:37   ` Sascha Hauer
2019-01-10 19:20     ` Andrey Smirnov
2019-01-09  7:12 ` [PATCH 11/21] soc: imx: gpcv2: correct PGC offset Andrey Smirnov
2019-01-09  7:12 ` [PATCH 12/21] reset: Add i.MX7 SRC reset driver Andrey Smirnov
2019-01-09  7:12 ` [PATCH 13/21] reset: imx7: Fix always writing bits as 0 Andrey Smirnov
2019-01-10  7:38   ` Sascha Hauer
2019-01-09  7:12 ` [PATCH 14/21] reset: Mark local functions as static Andrey Smirnov
2019-01-09  7:12 ` [PATCH 15/21] PCI: imx6: Add code to support i.MX7D Andrey Smirnov
2019-01-09  7:12 ` [PATCH 16/21] PCI: imx6: Allow probe deferral by reset GPIO Andrey Smirnov
2019-01-10  8:05   ` Sascha Hauer
2019-01-12  4:24     ` Andrey Smirnov
2019-01-09  7:12 ` [PATCH 17/21] PCI: imx6: Do not wait for speed change on i.MX7 Andrey Smirnov
2019-01-09  7:12 ` [PATCH 18/21] PCI: imx6: Do not switch speed if Gen2 is disabled Andrey Smirnov
2019-01-09  7:12 ` [PATCH 19/21] PCI: imx6: Fix spelling mistake: "contol" -> "control" Andrey Smirnov
2019-01-09  7:12 ` [PATCH 20/21] PCI: imx6: Drop unnecessary root_bus_nr setting Andrey Smirnov
2019-01-09  7:12 ` [PATCH 21/21] PCI: imx6: Port imx6_pcie_ltssm_enable() Andrey Smirnov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190109071210.18896-9-andrew.smirnov@gmail.com \
    --to=andrew.smirnov@gmail.com \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox