Browse Source

Merge branches 'pm-domains', 'pm-kconfig', 'pm-cpuidle' and 'powercap'

* pm-domains:
  PM / genpd: Stop/start devices without pm_runtime_force_suspend/resume()
  PM / domains: Don't skip driver's ->suspend|resume_noirq() callbacks
  PM / Domains: Remove obsolete "samsung,power-domain" check

* pm-kconfig:
  bus: simple-pm-bus: convert bool SIMPLE_PM_BUS to tristate
  PM: Provide a config snippet for disabling PM

* pm-cpuidle:
  cpuidle: Avoid NULL argument in cpuidle_switch_governor()

* powercap:
  powercap: intel_rapl: Fix trailing semicolon
  powercap: add suspend and resume mechanism for SOC power limit
  powercap: Simplify powercap_init()
Rafael J. Wysocki 7 years ago

+ 1 - 0
MAINTAINERS

@@ -10890,6 +10890,7 @@ F:	include/linux/pm.h
 F:	include/linux/pm_*
 F:	include/linux/pm_*
 F:	include/linux/powercap.h
 F:	include/linux/powercap.h
 F:	drivers/powercap/
 F:	drivers/powercap/
+F:	kernel/configs/nopm.config
 
 
 POWER STATE COORDINATION INTERFACE (PSCI)
 POWER STATE COORDINATION INTERFACE (PSCI)
 M:	Mark Rutland <mark.rutland@arm.com>
 M:	Mark Rutland <mark.rutland@arm.com>

+ 33 - 36
drivers/base/power/domain.c

@@ -1032,15 +1032,12 @@ static int genpd_prepare(struct device *dev)
 static int genpd_finish_suspend(struct device *dev, bool poweroff)
 static int genpd_finish_suspend(struct device *dev, bool poweroff)
 {
 {
 	struct generic_pm_domain *genpd;
 	struct generic_pm_domain *genpd;
-	int ret;
+	int ret = 0;
 
 
 	genpd = dev_to_genpd(dev);
 	genpd = dev_to_genpd(dev);
 	if (IS_ERR(genpd))
 	if (IS_ERR(genpd))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
-		return 0;
-
 	if (poweroff)
 	if (poweroff)
 		ret = pm_generic_poweroff_noirq(dev);
 		ret = pm_generic_poweroff_noirq(dev);
 	else
 	else
@@ -1048,10 +1045,19 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	if (genpd->dev_ops.stop && genpd->dev_ops.start) {
-		ret = pm_runtime_force_suspend(dev);
-		if (ret)
+	if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
+		return 0;
+
+	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+	    !pm_runtime_status_suspended(dev)) {
+		ret = genpd_stop_dev(genpd, dev);
+		if (ret) {
+			if (poweroff)
+				pm_generic_restore_noirq(dev);
+			else
+				pm_generic_resume_noirq(dev);
 			return ret;
 			return ret;
+		}
 	}
 	}
 
 
 	genpd_lock(genpd);
 	genpd_lock(genpd);
@@ -1085,7 +1091,7 @@ static int genpd_suspend_noirq(struct device *dev)
 static int genpd_resume_noirq(struct device *dev)
 static int genpd_resume_noirq(struct device *dev)
 {
 {
 	struct generic_pm_domain *genpd;
 	struct generic_pm_domain *genpd;
-	int ret = 0;
+	int ret;
 
 
 	dev_dbg(dev, "%s()\n", __func__);
 	dev_dbg(dev, "%s()\n", __func__);
 
 
@@ -1094,21 +1100,21 @@ static int genpd_resume_noirq(struct device *dev)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
 	if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
-		return 0;
+		return pm_generic_resume_noirq(dev);
 
 
 	genpd_lock(genpd);
 	genpd_lock(genpd);
 	genpd_sync_power_on(genpd, true, 0);
 	genpd_sync_power_on(genpd, true, 0);
 	genpd->suspended_count--;
 	genpd->suspended_count--;
 	genpd_unlock(genpd);
 	genpd_unlock(genpd);
 
 
-	if (genpd->dev_ops.stop && genpd->dev_ops.start)
-		ret = pm_runtime_force_resume(dev);
-
-	ret = pm_generic_resume_noirq(dev);
-	if (ret)
-		return ret;
+	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+	    !pm_runtime_status_suspended(dev)) {
+		ret = genpd_start_dev(genpd, dev);
+		if (ret)
+			return ret;
+	}
 
 
-	return ret;
+	return pm_generic_resume_noirq(dev);
 }
 }
 
 
 /**
 /**
@@ -1135,8 +1141,9 @@ static int genpd_freeze_noirq(struct device *dev)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	if (genpd->dev_ops.stop && genpd->dev_ops.start)
-		ret = pm_runtime_force_suspend(dev);
+	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+	    !pm_runtime_status_suspended(dev))
+		ret = genpd_stop_dev(genpd, dev);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -1159,8 +1166,9 @@ static int genpd_thaw_noirq(struct device *dev)
 	if (IS_ERR(genpd))
 	if (IS_ERR(genpd))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (genpd->dev_ops.stop && genpd->dev_ops.start) {
-		ret = pm_runtime_force_resume(dev);
+	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+	    !pm_runtime_status_suspended(dev)) {
+		ret = genpd_start_dev(genpd, dev);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 	}
 	}
@@ -1217,8 +1225,9 @@ static int genpd_restore_noirq(struct device *dev)
 	genpd_sync_power_on(genpd, true, 0);
 	genpd_sync_power_on(genpd, true, 0);
 	genpd_unlock(genpd);
 	genpd_unlock(genpd);
 
 
-	if (genpd->dev_ops.stop && genpd->dev_ops.start) {
-		ret = pm_runtime_force_resume(dev);
+	if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+	    !pm_runtime_status_suspended(dev)) {
+		ret = genpd_start_dev(genpd, dev);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 	}
 	}
@@ -2199,20 +2208,8 @@ int genpd_dev_pm_attach(struct device *dev)
 
 
 	ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
 	ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
 					"#power-domain-cells", 0, &pd_args);
 					"#power-domain-cells", 0, &pd_args);
-	if (ret < 0) {
-		if (ret != -ENOENT)
-			return ret;
-
-		/*
-		 * Try legacy Samsung-specific bindings
-		 * (for backwards compatibility of DT ABI)
-		 */
-		pd_args.args_count = 0;
-		pd_args.np = of_parse_phandle(dev->of_node,
-						"samsung,power-domain", 0);
-		if (!pd_args.np)
-			return -ENOENT;
-	}
+	if (ret < 0)
+		return ret;
 
 
 	mutex_lock(&gpd_list_lock);
 	mutex_lock(&gpd_list_lock);
 	pd = genpd_get_from_provider(&pd_args);
 	pd = genpd_get_from_provider(&pd_args);

+ 1 - 1
drivers/bus/Kconfig

@@ -120,7 +120,7 @@ config QCOM_EBI2
 	  SRAM, ethernet adapters, FPGAs and LCD displays.
 	  SRAM, ethernet adapters, FPGAs and LCD displays.
 
 
 config SIMPLE_PM_BUS
 config SIMPLE_PM_BUS
-	bool "Simple Power-Managed Bus Driver"
+	tristate "Simple Power-Managed Bus Driver"
 	depends on OF && PM
 	depends on OF && PM
 	help
 	help
 	  Driver for transparent busses that don't need a real driver, but
 	  Driver for transparent busses that don't need a real driver, but

+ 3 - 2
drivers/cpuidle/governor.c

@@ -36,14 +36,15 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
 /**
 /**
  * cpuidle_switch_governor - changes the governor
  * cpuidle_switch_governor - changes the governor
  * @gov: the new target governor
  * @gov: the new target governor
- *
- * NOTE: "gov" can be NULL to specify disabled
  * Must be called with cpuidle_lock acquired.
  * Must be called with cpuidle_lock acquired.
  */
  */
 int cpuidle_switch_governor(struct cpuidle_governor *gov)
 int cpuidle_switch_governor(struct cpuidle_governor *gov)
 {
 {
 	struct cpuidle_device *dev;
 	struct cpuidle_device *dev;
 
 
+	if (!gov)
+		return -EINVAL;
+
 	if (gov == cpuidle_curr_governor)
 	if (gov == cpuidle_curr_governor)
 		return 0;
 		return 0;
 
 

+ 98 - 1
drivers/powercap/intel_rapl.c

@@ -29,6 +29,7 @@
 #include <linux/sysfs.h>
 #include <linux/sysfs.h>
 #include <linux/cpu.h>
 #include <linux/cpu.h>
 #include <linux/powercap.h>
 #include <linux/powercap.h>
+#include <linux/suspend.h>
 #include <asm/iosf_mbi.h>
 #include <asm/iosf_mbi.h>
 
 
 #include <asm/processor.h>
 #include <asm/processor.h>
@@ -155,6 +156,7 @@ struct rapl_power_limit {
 	int prim_id; /* primitive ID used to enable */
 	int prim_id; /* primitive ID used to enable */
 	struct rapl_domain *domain;
 	struct rapl_domain *domain;
 	const char *name;
 	const char *name;
+	u64 last_power_limit;
 };
 };
 
 
 static const char pl1_name[] = "long_term";
 static const char pl1_name[] = "long_term";
@@ -1209,7 +1211,7 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
 	struct rapl_domain *rd;
 	struct rapl_domain *rd;
 	char dev_name[17]; /* max domain name = 7 + 1 + 8 for int + 1 for null*/
 	char dev_name[17]; /* max domain name = 7 + 1 + 8 for int + 1 for null*/
 	struct powercap_zone *power_zone = NULL;
 	struct powercap_zone *power_zone = NULL;
-	int nr_pl, ret;;
+	int nr_pl, ret;
 
 
 	/* Update the domain data of the new package */
 	/* Update the domain data of the new package */
 	rapl_update_domain_data(rp);
 	rapl_update_domain_data(rp);
@@ -1533,6 +1535,92 @@ static int rapl_cpu_down_prep(unsigned int cpu)
 
 
 static enum cpuhp_state pcap_rapl_online;
 static enum cpuhp_state pcap_rapl_online;
 
 
+static void power_limit_state_save(void)
+{
+	struct rapl_package *rp;
+	struct rapl_domain *rd;
+	int nr_pl, ret, i;
+
+	get_online_cpus();
+	list_for_each_entry(rp, &rapl_packages, plist) {
+		if (!rp->power_zone)
+			continue;
+		rd = power_zone_to_rapl_domain(rp->power_zone);
+		nr_pl = find_nr_power_limit(rd);
+		for (i = 0; i < nr_pl; i++) {
+			switch (rd->rpl[i].prim_id) {
+			case PL1_ENABLE:
+				ret = rapl_read_data_raw(rd,
+						POWER_LIMIT1,
+						true,
+						&rd->rpl[i].last_power_limit);
+				if (ret)
+					rd->rpl[i].last_power_limit = 0;
+				break;
+			case PL2_ENABLE:
+				ret = rapl_read_data_raw(rd,
+						POWER_LIMIT2,
+						true,
+						&rd->rpl[i].last_power_limit);
+				if (ret)
+					rd->rpl[i].last_power_limit = 0;
+				break;
+			}
+		}
+	}
+	put_online_cpus();
+}
+
+static void power_limit_state_restore(void)
+{
+	struct rapl_package *rp;
+	struct rapl_domain *rd;
+	int nr_pl, i;
+
+	get_online_cpus();
+	list_for_each_entry(rp, &rapl_packages, plist) {
+		if (!rp->power_zone)
+			continue;
+		rd = power_zone_to_rapl_domain(rp->power_zone);
+		nr_pl = find_nr_power_limit(rd);
+		for (i = 0; i < nr_pl; i++) {
+			switch (rd->rpl[i].prim_id) {
+			case PL1_ENABLE:
+				if (rd->rpl[i].last_power_limit)
+					rapl_write_data_raw(rd,
+						POWER_LIMIT1,
+						rd->rpl[i].last_power_limit);
+				break;
+			case PL2_ENABLE:
+				if (rd->rpl[i].last_power_limit)
+					rapl_write_data_raw(rd,
+						POWER_LIMIT2,
+						rd->rpl[i].last_power_limit);
+				break;
+			}
+		}
+	}
+	put_online_cpus();
+}
+
+static int rapl_pm_callback(struct notifier_block *nb,
+	unsigned long mode, void *_unused)
+{
+	switch (mode) {
+	case PM_SUSPEND_PREPARE:
+		power_limit_state_save();
+		break;
+	case PM_POST_SUSPEND:
+		power_limit_state_restore();
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block rapl_pm_notifier = {
+	.notifier_call = rapl_pm_callback,
+};
+
 static int __init rapl_init(void)
 static int __init rapl_init(void)
 {
 {
 	const struct x86_cpu_id *id;
 	const struct x86_cpu_id *id;
@@ -1560,8 +1648,16 @@ static int __init rapl_init(void)
 
 
 	/* Don't bail out if PSys is not supported */
 	/* Don't bail out if PSys is not supported */
 	rapl_register_psys();
 	rapl_register_psys();
+
+	ret = register_pm_notifier(&rapl_pm_notifier);
+	if (ret)
+		goto err_unreg_all;
+
 	return 0;
 	return 0;
 
 
+err_unreg_all:
+	cpuhp_remove_state(pcap_rapl_online);
+
 err_unreg:
 err_unreg:
 	rapl_unregister_powercap();
 	rapl_unregister_powercap();
 	return ret;
 	return ret;
@@ -1569,6 +1665,7 @@ err_unreg:
 
 
 static void __exit rapl_exit(void)
 static void __exit rapl_exit(void)
 {
 {
+	unregister_pm_notifier(&rapl_pm_notifier);
 	cpuhp_remove_state(pcap_rapl_online);
 	cpuhp_remove_state(pcap_rapl_online);
 	rapl_unregister_powercap();
 	rapl_unregister_powercap();
 }
 }

+ 2 - 4
drivers/powercap/powercap_sys.c

@@ -673,15 +673,13 @@ EXPORT_SYMBOL_GPL(powercap_unregister_control_type);
 
 
 static int __init powercap_init(void)
 static int __init powercap_init(void)
 {
 {
-	int result = 0;
+	int result;
 
 
 	result = seed_constraint_attributes();
 	result = seed_constraint_attributes();
 	if (result)
 	if (result)
 		return result;
 		return result;
 
 
-	result = class_register(&powercap_class);
-
-	return result;
+	return class_register(&powercap_class);
 }
 }
 
 
 device_initcall(powercap_init);
 device_initcall(powercap_init);

+ 15 - 0
kernel/configs/nopm.config

@@ -0,0 +1,15 @@
+CONFIG_PM=n
+CONFIG_SUSPEND=n
+CONFIG_HIBERNATION=n
+
+# Triggers PM on OMAP
+CONFIG_CPU_IDLE=n
+
+# Triggers enablement via hibernate callbacks
+CONFIG_XEN=n
+
+# ARM/ARM64 architectures that select PM unconditionally
+CONFIG_ARCH_OMAP2PLUS_TYPICAL=n
+CONFIG_ARCH_RENESAS=n
+CONFIG_ARCH_TEGRA=n
+CONFIG_ARCH_VEXPRESS=n