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"
......@@ -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
......
......@@ -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.
......
......@@ -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 @@ unlock:
}
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 */
......
......@@ -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) {}
......
......@@ -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) {
......
......@@ -126,6 +126,20 @@ void fix_up_intel_idle_driver_name(char *tmp, int num)
}
}
#ifdef __powerpc__
void map_power_idle_state_name(char *tmp)
{
if (!strncmp(tmp, "stop0_lite", CSTATE_NAME_LEN))
strcpy(tmp, "stop0L");
else if (!strncmp(tmp, "stop1_lite", CSTATE_NAME_LEN))
strcpy(tmp, "stop1L");
else if (!strncmp(tmp, "stop2_lite", CSTATE_NAME_LEN))
strcpy(tmp, "stop2L");
}
#else
void map_power_idle_state_name(char *tmp) { }
#endif
static struct cpuidle_monitor *cpuidle_register(void)
{
int num;
......@@ -145,6 +159,7 @@ static struct cpuidle_monitor *cpuidle_register(void)
if (tmp == NULL)
continue;
map_power_idle_state_name(tmp);
fix_up_intel_idle_driver_name(tmp, num);
strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
free(tmp);
......
......@@ -70,36 +70,43 @@ void print_n_spaces(int n)
printf(" ");
}
/* size of s must be at least n + 1 */
/*s is filled with left and right spaces
*to make its length atleast n+1
*/
int fill_string_with_spaces(char *s, int n)
{
char *temp;
int len = strlen(s);
if (len > n)
if (len >= n)
return -1;
temp = malloc(sizeof(char) * (n+1));
for (; len < n; len++)
s[len] = ' ';
s[len] = '\0';
snprintf(temp, n+1, " %s", s);
strcpy(s, temp);
free(temp);
return 0;
}
#define MAX_COL_WIDTH 6
void print_header(int topology_depth)
{
int unsigned mon;
int state, need_len;
cstate_t s;
char buf[128] = "";
int percent_width = 4;
fill_string_with_spaces(buf, topology_depth * 5 - 1);
printf("%s|", buf);
for (mon = 0; mon < avail_monitors; mon++) {
need_len = monitors[mon]->hw_states_num * (percent_width + 3)
need_len = monitors[mon]->hw_states_num * (MAX_COL_WIDTH + 1)
- 1;
if (mon != 0) {
printf("|| ");
need_len--;
}
if (mon != 0)
printf("||");
sprintf(buf, "%s", monitors[mon]->name);
fill_string_with_spaces(buf, need_len);
printf("%s", buf);
......@@ -107,23 +114,21 @@ void print_header(int topology_depth)
printf("\n");
if (topology_depth > 2)
printf("PKG |");
printf(" PKG|");
if (topology_depth > 1)
printf("CORE|");
if (topology_depth > 0)
printf("CPU |");
printf(" CPU|");
for (mon = 0; mon < avail_monitors; mon++) {
if (mon != 0)
printf("|| ");
else
printf(" ");
printf("||");
for (state = 0; state < monitors[mon]->hw_states_num; state++) {
if (state != 0)
printf(" | ");
printf("|");
s = monitors[mon]->hw_states[state];
sprintf(buf, "%s", s.name);
fill_string_with_spaces(buf, percent_width);
fill_string_with_spaces(buf, MAX_COL_WIDTH);
printf("%s", buf);
}
printf(" ");
......
......@@ -15,7 +15,16 @@
#define MONITORS_MAX 20
#define MONITOR_NAME_LEN 20
/* CSTATE_NAME_LEN is limited by header field width defined
* in cpupower-monitor.c. Header field width is defined to be
* sum of percent width and two spaces for padding.
*/
#ifdef __powerpc__
#define CSTATE_NAME_LEN 7
#else
#define CSTATE_NAME_LEN 5
#endif
#define CSTATE_DESC_LEN 60
int cpu_count;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment