2018-05-31 11:03:08

by Ulf Hansson

[permalink] [raw]
Subject: [PATCH v3 0/5] PM / Domains: Add support for multi PM domains per device

Changes in v3:
- Drop patch 1->4 as they have already been applied.
- Collected tags, for tests and reviews.
- Minor update to function descriptions in patch 4 (earlier 8) and 5
(earlier9).
- Note, because of the minor changes, no history is provided per patch.

Changes in v2:
- Addressed comments from Geert around DT doc.
- Addressed comments from Jon around clarification of how to use this
and changes to returned error codes.
- Fixed build error in case CONFIG_PM was unset.

There are devices that are partitioned across multiple PM domains. Currently
these can't be supported well by the available PM infrastructures we have in
the kernel. This series is an attempt to address this.

One existing case where devices are partitioned across multiple PM domains, is
the Nvida Tegra 124/210 X-USB subsystem. A while ago Jon Hunter (Nvidia) sent a
series, trying to address these issues, however this is a new approach, while
it re-uses the same concepts from DT point of view.

The Tegra 124/210 X-USB subsystem contains of a host controller and a device
controller. Each controller have its own independent PM domain, but are being
partitioned across another shared PM domain for the USB super-speed logic.

Currently to make the drivers work, either the related PM domains needs to stay
powered on always or the PM domain topology needs to be in-correctly modelled
through sub-domains. In both cases PM domains may be powered on while they
don't need to be, so in the end this means - wasting power -.

As stated above, this series intends to address these problem from a PM
infrastructure point of view. More details are available in each changelog.

Kind regards
Ulf Hansson

Ulf Hansson (5):
PM / Domains: dt: Allow power-domain property to be a list of
specifiers
PM / Domains: Don't attach devices in genpd with multi PM domains
PM / Domains: Split genpd_dev_pm_attach()
PM / Domains: Add support for multi PM domains per device to genpd
PM / Domains: Add dev_pm_domain_attach_by_id() to manage multi PM
domains

.../bindings/power/power_domain.txt | 19 ++-
drivers/base/power/common.c | 43 +++++-
drivers/base/power/domain.c | 134 +++++++++++++++---
include/linux/pm_domain.h | 15 ++
4 files changed, 183 insertions(+), 28 deletions(-)

--
2.17.0



2018-05-31 11:01:00

by Ulf Hansson

[permalink] [raw]
Subject: [PATCH v3 2/5] PM / Domains: Don't attach devices in genpd with multi PM domains

The power-domain DT property may now contain a list of PM domain
specifiers, which represents that a device are partitioned across multiple
PM domains. This leads to a new situation in genpd_dev_pm_attach(), as only
one PM domain can be attached per device.

To remain things simple for the most common configuration, when a single PM
domain is used, let's treat the multiple PM domain case as being specific.

In other words, let's change genpd_dev_pm_attach() to check for multiple PM
domains and prevent it from attach any PM domain for this case. Instead,
leave this to be managed separately, from following changes to genpd.

Cc: Rob Herring <[email protected]>
Cc: [email protected]
Suggested-by: Jon Hunter <[email protected]>
Signed-off-by: Ulf Hansson <[email protected]>
Acked-by: Jon Hunter <[email protected]>
Tested-by: Jon Hunter <[email protected]>
Reviewed-by: Viresh Kumar <[email protected]>
---
drivers/base/power/domain.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 6f403d6fccb2..908c44779ae7 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2229,10 +2229,10 @@ static void genpd_dev_pm_sync(struct device *dev)
* 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 a negative error code in case of failures. 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.
+ * 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 *dev)
{
@@ -2243,10 +2243,18 @@ int genpd_dev_pm_attach(struct device *dev)
if (!dev->of_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->of_node, "power-domains",
+ "#power-domain-cells") != 1)
+ return 0;
+
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", 0, &pd_args);
if (ret < 0)
- return 0;
+ return ret;

mutex_lock(&gpd_list_lock);
pd = genpd_get_from_provider(&pd_args);
--
2.17.0


2018-05-31 11:01:13

by Ulf Hansson

[permalink] [raw]
Subject: [PATCH v3 5/5] PM / Domains: Add dev_pm_domain_attach_by_id() to manage multi PM domains

The existing dev_pm_domain_attach() function, allows a single PM domain to
be attached per device. To be able to support devices that are partitioned
across multiple PM domains, let's introduce a new interface,
dev_pm_domain_attach_by_id().

The dev_pm_domain_attach_by_id() returns a new allocated struct device with
the corresponding attached PM domain. This enables for example a driver to
operate on the new device from a power management point of view. The driver
may then also benefit from using the received device, to set up so called
device-links towards its original device. Depending on the situation, these
links may then be dynamically changed.

The new interface is typically called by drivers during their probe phase,
in case they manages devices which uses multiple PM domains. If that is the
case, the driver also becomes responsible of managing the detaching of the
PM domains, which typically should be done at the remove phase. Detaching
is done by calling the existing dev_pm_domain_detach() function and for
each of the received devices from dev_pm_domain_attach_by_id().

Note, currently its only genpd that supports multiple PM domains per
device, but dev_pm_domain_attach_by_id() can easily by extended to cover
other PM domain types, if/when needed.

Signed-off-by: Ulf Hansson <[email protected]>
Acked-by: Jon Hunter <[email protected]>
Tested-by: Jon Hunter <[email protected]>
Reviewed-by: Viresh Kumar <[email protected]>
---
drivers/base/power/common.c | 43 ++++++++++++++++++++++++++++++++++---
include/linux/pm_domain.h | 7 ++++++
2 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 7ae62b6355b8..df41b4780b3b 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -116,14 +116,51 @@ int dev_pm_domain_attach(struct device *dev, bool power_on)
}
EXPORT_SYMBOL_GPL(dev_pm_domain_attach);

+/**
+ * dev_pm_domain_attach_by_id - Associate a device with one of its PM domains.
+ * @dev: The device used to lookup the PM domain.
+ * @index: The index of the PM domain.
+ *
+ * As @dev may only be attached to a single PM domain, the backend PM domain
+ * provider creates a virtual device to attach instead. If attachment succeeds,
+ * the ->detach() callback in the struct dev_pm_domain are assigned by the
+ * corresponding backend attach function, as to deal with detaching of the
+ * created virtual device.
+ *
+ * This function should typically be invoked by a driver during the probe phase,
+ * in case its device requires power management through multiple PM domains. The
+ * driver may benefit from using the received device, to configure device-links
+ * towards its original device. Depending on the use-case and if needed, the
+ * links may be dynamically changed by the driver, which allows it to control
+ * the power to the PM domains independently from each other.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns the virtual created device when successfully attached to its PM
+ * domain, NULL in case @dev don't need a PM domain, else an ERR_PTR().
+ * Note that, to detach the returned virtual device, the driver shall call
+ * dev_pm_domain_detach() on it, typically during the remove phase.
+ */
+struct device *dev_pm_domain_attach_by_id(struct device *dev,
+ unsigned int index)
+{
+ if (dev->pm_domain)
+ return ERR_PTR(-EEXIST);
+
+ return genpd_dev_pm_attach_by_id(dev, index);
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_id);
+
/**
* dev_pm_domain_detach - Detach a device from its PM domain.
* @dev: Device to detach.
* @power_off: Used to indicate whether we should power off the device.
*
- * This functions will reverse the actions from dev_pm_domain_attach() and thus
- * try to detach the @dev from its PM domain. Typically it should be invoked
- * from subsystem level code during the remove phase.
+ * This functions will reverse the actions from dev_pm_domain_attach() and
+ * dev_pm_domain_attach_by_id(), thus it detaches @dev from its PM domain.
+ * Typically it should be invoked during the remove phase, either from
+ * subsystem level code or from drivers.
*
* Callers must ensure proper synchronization of this function with power
* management callbacks.
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 82458e8e2e01..9206a4fef9ac 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -299,6 +299,8 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)

#ifdef CONFIG_PM
int dev_pm_domain_attach(struct device *dev, bool power_on);
+struct device *dev_pm_domain_attach_by_id(struct device *dev,
+ unsigned int index);
void dev_pm_domain_detach(struct device *dev, bool power_off);
void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd);
#else
@@ -306,6 +308,11 @@ static inline int dev_pm_domain_attach(struct device *dev, bool power_on)
{
return 0;
}
+static inline struct device *dev_pm_domain_attach_by_id(struct device *dev,
+ unsigned int index)
+{
+ return NULL;
+}
static inline void dev_pm_domain_detach(struct device *dev, bool power_off) {}
static inline void dev_pm_domain_set(struct device *dev,
struct dev_pm_domain *pd) {}
--
2.17.0


2018-05-31 11:01:29

by Ulf Hansson

[permalink] [raw]
Subject: [PATCH v3 4/5] PM / Domains: Add support for multi PM domains per device to genpd

To support devices being partitioned across multiple PM domains, let's
begin with extending genpd to cope with these kind of configurations.

Therefore, add a new exported function genpd_dev_pm_attach_by_id(), which
is similar to the existing genpd_dev_pm_attach(), but with the difference
that it allows its callers to provide an index to the PM domain that it
wants to attach.

Note that, genpd_dev_pm_attach_by_id() shall only be called by the driver
core / PM core, similar to how the existing dev_pm_domain_attach() makes
use of genpd_dev_pm_attach(). However, this is implemented by following
changes on top.

Because, only one PM domain can be attached per device, genpd needs to
create a virtual device that it can attach/detach instead. More precisely,
let the new function genpd_dev_pm_attach_by_id() register a virtual struct
device via calling device_register(). Then let it attach this device to the
corresponding PM domain, rather than the one that is provided by the
caller. The actual attaching is done via re-using the existing genpd OF
functions.

At successful attachment, genpd_dev_pm_attach_by_id() returns the created
virtual device, which allows the caller to operate on it to deal with power
management. Following changes on top, provides more details in this
regards.

To deal with detaching of a PM domain for the multiple PM domains case,
let's also extend the existing genpd_dev_pm_detach() function, to cover the
cleanup of the created virtual device, via make it call device_unregister()
on it. In this way, there is no need to introduce a new function to deal
with detach for the multiple PM domain case, but instead the existing one
is re-used.

Signed-off-by: Ulf Hansson <[email protected]>
Acked-by: Jon Hunter <[email protected]>
Tested-by: Jon Hunter <[email protected]>
Reviewed-by: Viresh Kumar <[email protected]>
---
drivers/base/power/domain.c | 80 +++++++++++++++++++++++++++++++++++++
include/linux/pm_domain.h | 8 ++++
2 files changed, 88 insertions(+)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index b1fcbf917974..4925af5c4cf0 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2171,6 +2171,15 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
}
EXPORT_SYMBOL_GPL(of_genpd_remove_last);

+static void genpd_release_dev(struct device *dev)
+{
+ kfree(dev);
+}
+
+static struct bus_type genpd_bus_type = {
+ .name = "genpd",
+};
+
/**
* genpd_dev_pm_detach - Detach a device from its PM domain.
* @dev: Device to detach.
@@ -2208,6 +2217,10 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)

/* Check if PM domain can be powered off after removing this device. */
genpd_queue_power_off_work(pd);
+
+ /* Unregister the device if it was created by genpd. */
+ if (dev->bus == &genpd_bus_type)
+ device_unregister(dev);
}

static void genpd_dev_pm_sync(struct device *dev)
@@ -2298,6 +2311,67 @@ int genpd_dev_pm_attach(struct device *dev)
}
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);

+/**
+ * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains.
+ * @dev: The device used to lookup the PM domain.
+ * @index: The index of the PM domain.
+ *
+ * Parse device's OF node to find a PM domain specifier at the provided @index.
+ * If such is found, creates a virtual device and attaches it to the retrieved
+ * pm_domain ops. To deal with detaching of the virtual device, the ->detach()
+ * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach().
+ *
+ * Returns the created virtual device if successfully attached PM domain, NULL
+ * when the device don't need a PM domain, else an ERR_PTR() in case of
+ * failures. If a power-domain exists for the device, but cannot be found or
+ * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device
+ * is not probed and to re-try again later.
+ */
+struct device *genpd_dev_pm_attach_by_id(struct device *dev,
+ unsigned int index)
+{
+ struct device *genpd_dev;
+ int num_domains;
+ int ret;
+
+ if (!dev->of_node)
+ return NULL;
+
+ /* Deal only with devices using multiple PM domains. */
+ num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
+ "#power-domain-cells");
+ if (num_domains < 2 || index >= num_domains)
+ return NULL;
+
+ /* Allocate and register device on the genpd bus. */
+ genpd_dev = kzalloc(sizeof(*genpd_dev), GFP_KERNEL);
+ if (!genpd_dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev_set_name(genpd_dev, "genpd:%u:%s", index, dev_name(dev));
+ genpd_dev->bus = &genpd_bus_type;
+ genpd_dev->release = genpd_release_dev;
+
+ ret = device_register(genpd_dev);
+ if (ret) {
+ kfree(genpd_dev);
+ return ERR_PTR(ret);
+ }
+
+ /* Try to attach the device to the PM domain at the specified index. */
+ ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index);
+ if (ret < 1) {
+ device_unregister(genpd_dev);
+ return ret ? ERR_PTR(ret) : NULL;
+ }
+
+ pm_runtime_set_active(genpd_dev);
+ pm_runtime_enable(genpd_dev);
+
+ return genpd_dev;
+}
+EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
+
static const struct of_device_id idle_state_match[] = {
{ .compatible = "domain-idle-state", },
{ }
@@ -2457,6 +2531,12 @@ unsigned int of_genpd_opp_to_performance_state(struct device *dev,
}
EXPORT_SYMBOL_GPL(of_genpd_opp_to_performance_state);

+static int __init genpd_bus_init(void)
+{
+ return bus_register(&genpd_bus_type);
+}
+core_initcall(genpd_bus_init);
+
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */


diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 42e0d649e653..82458e8e2e01 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -237,6 +237,8 @@ unsigned int of_genpd_opp_to_performance_state(struct device *dev,
struct device_node *opp_node);

int genpd_dev_pm_attach(struct device *dev);
+struct device *genpd_dev_pm_attach_by_id(struct device *dev,
+ unsigned int index);
#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
static inline int of_genpd_add_provider_simple(struct device_node *np,
struct generic_pm_domain *genpd)
@@ -282,6 +284,12 @@ static inline int genpd_dev_pm_attach(struct device *dev)
return 0;
}

+static inline struct device *genpd_dev_pm_attach_by_id(struct device *dev,
+ unsigned int index)
+{
+ return NULL;
+}
+
static inline
struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
{
--
2.17.0


2018-05-31 11:02:28

by Ulf Hansson

[permalink] [raw]
Subject: [PATCH v3 1/5] PM / Domains: dt: Allow power-domain property to be a list of specifiers

To be able to describe topologies where devices are partitioned across
multiple power domains, let's extend the power-domain property to allow
being a list of PM domain specifiers.

Cc: Rob Herring <[email protected]>
Cc: [email protected]
Suggested-by: Jon Hunter <[email protected]>
Signed-off-by: Ulf Hansson <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
Reviewed-by: Viresh Kumar <[email protected]>
---
.../bindings/power/power_domain.txt | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
index 4733f76cbe48..9b387f861aed 100644
--- a/Documentation/devicetree/bindings/power/power_domain.txt
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -111,8 +111,8 @@ Example 3:
==PM domain consumers==

Required properties:
- - power-domains : A phandle and PM domain specifier as defined by bindings of
- the power controller specified by phandle.
+ - power-domains : A list of PM domain specifiers, as defined by bindings of
+ the power controller that is the PM domain provider.

Example:

@@ -122,9 +122,18 @@ Example:
power-domains = <&power 0>;
};

-The node above defines a typical PM domain consumer device, which is located
-inside a PM domain with index 0 of a power controller represented by a node
-with the label "power".
+ leaky-device@12351000 {
+ compatible = "foo,i-leak-current";
+ reg = <0x12351000 0x1000>;
+ power-domains = <&power 0>, <&power 1> ;
+ };
+
+The first example above defines a typical PM domain consumer device, which is
+located inside a PM domain with index 0 of a power controller represented by a
+node with the label "power".
+In the second example the consumer device are partitioned across two PM domains,
+the first with index 0 and the second with index 1, of a power controller that
+is represented by a node with the label "power.

Optional properties:
- required-opps: This contains phandle to an OPP node in another device's OPP
--
2.17.0


2018-05-31 11:02:56

by Ulf Hansson

[permalink] [raw]
Subject: [PATCH v3 3/5] PM / Domains: Split genpd_dev_pm_attach()

To extend genpd to deal with allowing multiple PM domains per device, some
of the code in genpd_dev_pm_attach() can be re-used. Let's prepare for this
by moving some of the code into a sub-function.

Signed-off-by: Ulf Hansson <[email protected]>
Acked-by: Jon Hunter <[email protected]>
Tested-by: Jon Hunter <[email protected]>
Reviewed-by: Viresh Kumar <[email protected]>
---
drivers/base/power/domain.c | 60 ++++++++++++++++++++-----------------
1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 908c44779ae7..b1fcbf917974 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2221,38 +2221,15 @@ static void genpd_dev_pm_sync(struct device *dev)
genpd_queue_power_off_work(pd);
}

-/**
- * 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 *dev)
+static int __genpd_dev_pm_attach(struct device *dev, struct device_node *np,
+ unsigned int index)
{
struct of_phandle_args pd_args;
struct generic_pm_domain *pd;
int ret;

- if (!dev->of_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->of_node, "power-domains",
- "#power-domain-cells") != 1)
- return 0;
-
- ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
- "#power-domain-cells", 0, &pd_args);
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", index, &pd_args);
if (ret < 0)
return ret;

@@ -2290,6 +2267,35 @@ int genpd_dev_pm_attach(struct device *dev)

return ret ? -EPROBE_DEFER : 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 *dev)
+{
+ if (!dev->of_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->of_node, "power-domains",
+ "#power-domain-cells") != 1)
+ return 0;
+
+ return __genpd_dev_pm_attach(dev, dev->of_node, 0);
+}
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);

static const struct of_device_id idle_state_match[] = {
--
2.17.0


2018-05-31 11:41:43

by Lucas Stach

[permalink] [raw]
Subject: Re: [PATCH v3 4/5] PM / Domains: Add support for multi PM domains per device to genpd

Hi Ulf,

Am Donnerstag, den 31.05.2018, 12:59 +0200 schrieb Ulf Hansson:
> To support devices being partitioned across multiple PM domains, let's
> begin with extending genpd to cope with these kind of configurations.
>
> Therefore, add a new exported function genpd_dev_pm_attach_by_id(), which
> is similar to the existing genpd_dev_pm_attach(), but with the difference
> that it allows its callers to provide an index to the PM domain that it
> wants to attach.
>
> Note that, genpd_dev_pm_attach_by_id() shall only be called by the driver
> core / PM core, similar to how the existing dev_pm_domain_attach() makes
> use of genpd_dev_pm_attach(). However, this is implemented by following
> changes on top.

by_id() APIs are not really intuitive to use for driver writers. Other
subsystems have solved this by providing a "-names" property to give
the phandles a bit more meaning and then providing a by_name API. I
would really appreciate if PM domains could move in the same direction.

Regards,
Lucas

> Because, only one PM domain can be attached per device, genpd needs to
> create a virtual device that it can attach/detach instead. More precisely,
> let the new function genpd_dev_pm_attach_by_id() register a virtual struct
> device via calling device_register(). Then let it attach this device to the
> corresponding PM domain, rather than the one that is provided by the
> caller. The actual attaching is done via re-using the existing genpd OF
> functions.
>
> At successful attachment, genpd_dev_pm_attach_by_id() returns the created
> virtual device, which allows the caller to operate on it to deal with power
> management. Following changes on top, provides more details in this
> regards.
>
> To deal with detaching of a PM domain for the multiple PM domains case,
> let's also extend the existing genpd_dev_pm_detach() function, to cover the
> cleanup of the created virtual device, via make it call device_unregister()
> on it. In this way, there is no need to introduce a new function to deal
> with detach for the multiple PM domain case, but instead the existing one
> is re-used.
>
> Signed-off-by: Ulf Hansson <[email protected]>
> Acked-by: Jon Hunter <[email protected]>
> Tested-by: Jon Hunter <[email protected]>
> Reviewed-by: Viresh Kumar <[email protected]>
> ---
> drivers/base/power/domain.c | 80 +++++++++++++++++++++++++++++++++++++
> include/linux/pm_domain.h | 8 ++++
> 2 files changed, 88 insertions(+)
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index b1fcbf917974..4925af5c4cf0 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -2171,6 +2171,15 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
> }
> EXPORT_SYMBOL_GPL(of_genpd_remove_last);
>
> +static void genpd_release_dev(struct device *dev)
> +{
> + kfree(dev);
> +}
> +
> +static struct bus_type genpd_bus_type = {
> + .name = "genpd",
> +};
> +
> /**
> * genpd_dev_pm_detach - Detach a device from its PM domain.
> * @dev: Device to detach.
> @@ -2208,6 +2217,10 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
>
> /* Check if PM domain can be powered off after removing this device. */
> genpd_queue_power_off_work(pd);
> +
> + /* Unregister the device if it was created by genpd. */
> + if (dev->bus == &genpd_bus_type)
> + device_unregister(dev);
> }
>
> static void genpd_dev_pm_sync(struct device *dev)
> @@ -2298,6 +2311,67 @@ int genpd_dev_pm_attach(struct device *dev)
> }
> EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
>
> +/**
> + * genpd_dev_pm_attach_by_id - Associate a device with one of its PM domains.
> + * @dev: The device used to lookup the PM domain.
> + * @index: The index of the PM domain.
> + *
> + * Parse device's OF node to find a PM domain specifier at the provided @index.
> + * If such is found, creates a virtual device and attaches it to the retrieved
> + * pm_domain ops. To deal with detaching of the virtual device, the ->detach()
> + * callback in the struct dev_pm_domain are assigned to genpd_dev_pm_detach().
> + *
> + * Returns the created virtual device if successfully attached PM domain, NULL
> + * when the device don't need a PM domain, else an ERR_PTR() in case of
> + * failures. If a power-domain exists for the device, but cannot be found or
> + * turned on, then ERR_PTR(-EPROBE_DEFER) is returned to ensure that the device
> + * is not probed and to re-try again later.
> + */
> +struct device *genpd_dev_pm_attach_by_id(struct device *dev,
> + unsigned int index)
> +{
> + struct device *genpd_dev;
> + int num_domains;
> + int ret;
> +
> + if (!dev->of_node)
> + return NULL;
> +
> + /* Deal only with devices using multiple PM domains. */
> + num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
> + "#power-domain-cells");
> + if (num_domains < 2 || index >= num_domains)
> + return NULL;
> +
> + /* Allocate and register device on the genpd bus. */
> + genpd_dev = kzalloc(sizeof(*genpd_dev), GFP_KERNEL);
> + if (!genpd_dev)
> + return ERR_PTR(-ENOMEM);
> +
> + dev_set_name(genpd_dev, "genpd:%u:%s", index, dev_name(dev));
> + genpd_dev->bus = &genpd_bus_type;
> + genpd_dev->release = genpd_release_dev;
> +
> + ret = device_register(genpd_dev);
> + if (ret) {
> + kfree(genpd_dev);
> + return ERR_PTR(ret);
> + }
> +
> + /* Try to attach the device to the PM domain at the specified index. */
> + ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index);
> + if (ret < 1) {
> + device_unregister(genpd_dev);
> + return ret ? ERR_PTR(ret) : NULL;
> + }
> +
> + pm_runtime_set_active(genpd_dev);
> + pm_runtime_enable(genpd_dev);
> +
> + return genpd_dev;
> +}
> +EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
> +
> static const struct of_device_id idle_state_match[] = {
> { .compatible = "domain-idle-state", },
> { }
> @@ -2457,6 +2531,12 @@ unsigned int of_genpd_opp_to_performance_state(struct device *dev,
> }
> EXPORT_SYMBOL_GPL(of_genpd_opp_to_performance_state);
>
> +static int __init genpd_bus_init(void)
> +{
> + return bus_register(&genpd_bus_type);
> +}
> +core_initcall(genpd_bus_init);
> +
> #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
>
>
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 42e0d649e653..82458e8e2e01 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -237,6 +237,8 @@ unsigned int of_genpd_opp_to_performance_state(struct device *dev,
> struct device_node *opp_node);
>
> int genpd_dev_pm_attach(struct device *dev);
> +struct device *genpd_dev_pm_attach_by_id(struct device *dev,
> + unsigned int index);
> #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
> static inline int of_genpd_add_provider_simple(struct device_node *np,
> struct generic_pm_domain *genpd)
> @@ -282,6 +284,12 @@ static inline int genpd_dev_pm_attach(struct device *dev)
> return 0;
> }
>
> +static inline struct device *genpd_dev_pm_attach_by_id(struct device *dev,
> + unsigned int index)
> +{
> + return NULL;
> +}
> +
> static inline
> struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
> {

2018-05-31 12:49:50

by Jon Hunter

[permalink] [raw]
Subject: Re: [PATCH v3 4/5] PM / Domains: Add support for multi PM domains per device to genpd


On 31/05/18 12:40, Lucas Stach wrote:
> Hi Ulf,
>
> Am Donnerstag, den 31.05.2018, 12:59 +0200 schrieb Ulf Hansson:
>> To support devices being partitioned across multiple PM domains, let's
>> begin with extending genpd to cope with these kind of configurations.
>>
>> Therefore, add a new exported function genpd_dev_pm_attach_by_id(), which
>> is similar to the existing genpd_dev_pm_attach(), but with the difference
>> that it allows its callers to provide an index to the PM domain that it
>> wants to attach.
>>
>> Note that, genpd_dev_pm_attach_by_id() shall only be called by the driver
>> core / PM core, similar to how the existing dev_pm_domain_attach() makes
>> use of genpd_dev_pm_attach(). However, this is implemented by following
>> changes on top.
>
> by_id() APIs are not really intuitive to use for driver writers. Other
> subsystems have solved this by providing a "-names" property to give
> the phandles a bit more meaning and then providing a by_name API. I
> would really appreciate if PM domains could move in the same direction.

As discussed here [0], there are plans to add that.

Cheers
Jon

[0] https://patchwork.ozlabs.org/patch/921938/

--
nvpublic

2018-06-12 15:01:11

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] PM / Domains: Add support for multi PM domains per device

On Thursday, May 31, 2018 12:59:54 PM CEST Ulf Hansson wrote:
> Changes in v3:
> - Drop patch 1->4 as they have already been applied.
> - Collected tags, for tests and reviews.
> - Minor update to function descriptions in patch 4 (earlier 8) and 5
> (earlier9).
> - Note, because of the minor changes, no history is provided per patch.
>
> Changes in v2:
> - Addressed comments from Geert around DT doc.
> - Addressed comments from Jon around clarification of how to use this
> and changes to returned error codes.
> - Fixed build error in case CONFIG_PM was unset.
>
> There are devices that are partitioned across multiple PM domains. Currently
> these can't be supported well by the available PM infrastructures we have in
> the kernel. This series is an attempt to address this.
>
> One existing case where devices are partitioned across multiple PM domains, is
> the Nvida Tegra 124/210 X-USB subsystem. A while ago Jon Hunter (Nvidia) sent a
> series, trying to address these issues, however this is a new approach, while
> it re-uses the same concepts from DT point of view.
>
> The Tegra 124/210 X-USB subsystem contains of a host controller and a device
> controller. Each controller have its own independent PM domain, but are being
> partitioned across another shared PM domain for the USB super-speed logic.
>
> Currently to make the drivers work, either the related PM domains needs to stay
> powered on always or the PM domain topology needs to be in-correctly modelled
> through sub-domains. In both cases PM domains may be powered on while they
> don't need to be, so in the end this means - wasting power -.
>
> As stated above, this series intends to address these problem from a PM
> infrastructure point of view. More details are available in each changelog.
>
> Kind regards
> Ulf Hansson
>
> Ulf Hansson (5):
> PM / Domains: dt: Allow power-domain property to be a list of
> specifiers
> PM / Domains: Don't attach devices in genpd with multi PM domains
> PM / Domains: Split genpd_dev_pm_attach()
> PM / Domains: Add support for multi PM domains per device to genpd
> PM / Domains: Add dev_pm_domain_attach_by_id() to manage multi PM
> domains
>
> .../bindings/power/power_domain.txt | 19 ++-
> drivers/base/power/common.c | 43 +++++-
> drivers/base/power/domain.c | 134 +++++++++++++++---
> include/linux/pm_domain.h | 15 ++
> 4 files changed, 183 insertions(+), 28 deletions(-)
>
>

Applied, thanks!