Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a900f88 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'pm-domains' and 'pm-tools'

Additional updates of the generic power domains (genpd) framework
(support for devices attached to multiple domains) and the cpupower
utility (minor fixes) for 4.18-rc1.

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

* pm-tools:
  cpupower : Fix header name to read idle state name
  cpupower: fix spelling mistake: "logilename" -> "logfilename"
Loading
Loading
Loading
Loading
+14 −5
Original line number Diff line number Diff line
@@ -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
+40 −3
Original line number Diff line number Diff line
@@ -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.
+114 −20
Original line number Diff line number Diff line
@@ -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)
@@ -2221,32 +2234,17 @@ 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 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.
 */
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;

	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 0;
		return ret;

	mutex_lock(&gpd_list_lock);
	pd = genpd_get_from_provider(&pd_args);
@@ -2282,8 +2280,98 @@ 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);

/**
 * 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", },
	{ }
@@ -2443,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 */


+15 −0
Original line number Diff line number Diff line
@@ -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)
{
@@ -291,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
@@ -298,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) {}
+1 −1
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ FILE *prepare_output(const char *dirname)
			dirname, time(NULL));
	}

	dprintf("logilename: %s\n", filename);
	dprintf("logfilename: %s\n", filename);

	output = fopen(filename, "w+");
	if (output == NULL) {
Loading