cpupower: Introduce idle-set subcommand and C-state enabling/disabling

Example:

cpupower idle-set -d 3

will disable C-state 3 on all processors (set commands are active on
all CPUs by default), same as:

cpupower -c all idle-set -d 3

Signed-off-by: Thomas Renninger <trenn@suse.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
diff --git a/tools/power/cpupower/utils/builtin.h b/tools/power/cpupower/utils/builtin.h
index c10496f..2284c8e 100644
--- a/tools/power/cpupower/utils/builtin.h
+++ b/tools/power/cpupower/utils/builtin.h
@@ -5,6 +5,7 @@
 extern int cmd_info(int argc, const char **argv);
 extern int cmd_freq_set(int argc, const char **argv);
 extern int cmd_freq_info(int argc, const char **argv);
+extern int cmd_idle_set(int argc, const char **argv);
 extern int cmd_idle_info(int argc, const char **argv);
 extern int cmd_monitor(int argc, const char **argv);
 
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c
index edd5dba..75e66de 100644
--- a/tools/power/cpupower/utils/cpuidle-info.c
+++ b/tools/power/cpupower/utils/cpuidle-info.c
@@ -48,10 +48,14 @@
 		return;
 
 	for (idlestate = 0; idlestate < idlestates; idlestate++) {
+		int disabled = sysfs_is_idlestate_disabled(cpu, idlestate);
+		/* Disabled interface not supported on older kernels */
+		if (disabled < 0)
+			disabled = 0;
 		tmp = sysfs_get_idlestate_name(cpu, idlestate);
 		if (!tmp)
 			continue;
-		printf("%s:\n", tmp);
+		printf("%s%s:\n", tmp, (disabled) ? " (DISABLED) " : "");
 		free(tmp);
 
 		tmp = sysfs_get_idlestate_desc(cpu, idlestate);
diff --git a/tools/power/cpupower/utils/cpuidle-set.c b/tools/power/cpupower/utils/cpuidle-set.c
new file mode 100644
index 0000000..c78141c
--- /dev/null
+++ b/tools/power/cpupower/utils/cpuidle-set.c
@@ -0,0 +1,118 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <getopt.h>
+
+#include "cpufreq.h"
+#include "helpers/helpers.h"
+#include "helpers/sysfs.h"
+
+static struct option info_opts[] = {
+	{ .name = "disable",	.has_arg = required_argument,	.flag = NULL,	.val = 'd'},
+	{ .name = "enable",	.has_arg = required_argument,	.flag = NULL,	.val = 'e'},
+	{ },
+};
+
+
+int cmd_idle_set(int argc, char **argv)
+{
+	extern char *optarg;
+	extern int optind, opterr, optopt;
+	int ret = 0, cont = 1, param = 0, idlestate = 0;
+	unsigned int cpu = 0;
+
+	do {
+		ret = getopt_long(argc, argv, "d:e:", info_opts, NULL);
+		if (ret == -1)
+			break;
+		switch (ret) {
+		case '?':
+			param = '?';
+			cont = 0;
+			break;
+		case 'd':
+			if (param) {
+				param = -1;
+				cont = 0;
+				break;
+			}
+			param = ret;
+			idlestate = atoi(optarg);
+			break;
+		case 'e':
+			if (param) {
+				param = -1;
+				cont = 0;
+				break;
+			}
+			param = ret;
+			idlestate = atoi(optarg);
+			break;
+		case -1:
+			cont = 0;
+			break;
+		}
+	} while (cont);
+
+	switch (param) {
+	case -1:
+		printf(_("You can't specify more than one "
+			 "output-specific argument\n"));
+		exit(EXIT_FAILURE);
+	case '?':
+		printf(_("invalid or unknown argument\n"));
+		exit(EXIT_FAILURE);
+	}
+
+	/* Default is: set all CPUs */
+	if (bitmask_isallclear(cpus_chosen))
+		bitmask_setall(cpus_chosen);
+
+	for (cpu = bitmask_first(cpus_chosen);
+	     cpu <= bitmask_last(cpus_chosen); cpu++) {
+
+		if (!bitmask_isbitset(cpus_chosen, cpu))
+			continue;
+
+		switch (param) {
+
+		case 'd':
+			ret = sysfs_idlestate_disable(cpu, idlestate, 1);
+			if (ret == 0)
+		printf(_("Idlestate %u disabled on CPU %u\n"),  idlestate, cpu);
+			else if (ret == -1)
+		printf(_("Idlestate %u not available on CPU %u\n"),
+		       idlestate, cpu);
+			else if (ret == -2)
+		printf(_("Idlestate disabling not supported by kernel\n"));
+			else
+		printf(_("Idlestate %u not disabled on CPU %u\n"),
+		       idlestate, cpu);
+			break;
+		case 'e':
+			ret = sysfs_idlestate_disable(cpu, idlestate, 0);
+			if (ret == 0)
+		printf(_("Idlestate %u enabled on CPU %u\n"),  idlestate, cpu);
+			else if (ret == -1)
+		printf(_("Idlestate %u not available on CPU %u\n"),
+		       idlestate, cpu);
+			else if (ret == -2)
+		printf(_("Idlestate enabling not supported by kernel\n"));
+			else
+		printf(_("Idlestate %u not enabled on CPU %u\n"),
+		       idlestate, cpu);
+			break;
+		default:
+			/* Not reachable with proper args checking */
+			printf(_("Invalid or unknown argument\n"));
+			exit(EXIT_FAILURE);
+			break;
+		}
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c
index 52bee59..7efc570 100644
--- a/tools/power/cpupower/utils/cpupower.c
+++ b/tools/power/cpupower/utils/cpupower.c
@@ -17,12 +17,6 @@
 #include "helpers/helpers.h"
 #include "helpers/bitmask.h"
 
-struct cmd_struct {
-	const char *cmd;
-	int (*main)(int, const char **);
-	int needs_root;
-};
-
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
 static int cmd_help(int argc, const char **argv);
@@ -43,10 +37,17 @@
 
 static void print_help(void);
 
+struct cmd_struct {
+	const char *cmd;
+	int (*main)(int, const char **);
+	int needs_root;
+};
+
 static struct cmd_struct commands[] = {
 	{ "frequency-info",	cmd_freq_info,	0	},
 	{ "frequency-set",	cmd_freq_set,	1	},
 	{ "idle-info",		cmd_idle_info,	0	},
+	{ "idle-set",		cmd_idle_set,	1	},
 	{ "set",		cmd_set,	1	},
 	{ "info",		cmd_info,	0	},
 	{ "monitor",		cmd_monitor,	0	},