diff mbox

[2/2] intel_frequency: A tool to manipulate Intel GPU frequency

Message ID 1420949969-877-2-git-send-email-benjamin.widawsky@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ben Widawsky Jan. 11, 2015, 4:19 a.m. UTC
WARNING: very minimally tested

In general you should not need this tool. It's primary purpose is for
benchmarking, and for debugging performance issues.

For many kernel releases now sysfs has supported reading and writing the GPU
frequency. Therefore, this tool provides no new functionality. What it does
provide is an easy to package (for distros) tool that handles the most common
scenarios.

Signed-off-by: Ben Widawsky <ben@bwidawsk.net>

Here are some sample usages:

$ sudo intel_frequency --get=cur,min,max,eff
cur: 200 MHz
min: 200 MHz
RP1: 200 MHz
max: 1200 MHz

$ sudo intel_frequency -g
cur: 200 MHz
min: 200 MHz
RP1: 200 MHz
max: 1200 MHz

$ sudo intel_frequency -geff
RP1: 200 MHz

$ sudo intel_frequency --set min=300
$ sudo intel_frequency --get min
cur: 300 MHz
min: 300 MHz
RP1: 200 MHz
max: 1200 MHz

$ sudo intel_frequency --custom max=900
$ sudo intel_frequency --get max
cur: 300 MHz
min: 300 MHz
RP1: 200 MHz
max: 900 MHz

$ sudo ./tools/intel_frequency --max
$ sudo ./tools/intel_frequency -g
cur: 1200 MHz
min: 1200 MHz
RP1: 200 MHz
max: 1200 MHz

$ sudo intel_frequency -e
$ sudo intel_frequency -g
cur: 200 MHz
min: 200 MHz
RP1: 200 MHz
max: 200 MHz

$ sudo intel_frequency --max
$ sudo intel_frequency -g
cur: 1200 MHz
min: 1200 MHz
RP1: 200 MHz
max: 1200 MHz

$ sudo intel_frequency --min
$ sudo intel_frequency -g
cur: 200 MHz
min: 200 MHz
RP1: 200 MHz
max: 200 MHz
---
 tools/Makefile.sources  |   1 +
 tools/intel_frequency.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 326 insertions(+)
 create mode 100644 tools/intel_frequency.c

Comments

Ben Widawsky Jan. 11, 2015, 4:36 a.m. UTC | #1
Ken gave me the good idea of -d, --defaults to go back to the hardware specified
defaults. I will send out a v2 if nobody else has any complaints.

On Sat, Jan 10, 2015 at 08:19:29PM -0800, Ben Widawsky wrote:
> WARNING: very minimally tested
> 
> In general you should not need this tool. It's primary purpose is for
> benchmarking, and for debugging performance issues.
> 
> For many kernel releases now sysfs has supported reading and writing the GPU
> frequency. Therefore, this tool provides no new functionality. What it does
> provide is an easy to package (for distros) tool that handles the most common
> scenarios.
> 
> Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
> 
> Here are some sample usages:
> 
> $ sudo intel_frequency --get=cur,min,max,eff
> cur: 200 MHz
> min: 200 MHz
> RP1: 200 MHz
> max: 1200 MHz
> 
> $ sudo intel_frequency -g
> cur: 200 MHz
> min: 200 MHz
> RP1: 200 MHz
> max: 1200 MHz
> 
> $ sudo intel_frequency -geff
> RP1: 200 MHz
> 
> $ sudo intel_frequency --set min=300
> $ sudo intel_frequency --get min
> cur: 300 MHz
> min: 300 MHz
> RP1: 200 MHz
> max: 1200 MHz
> 
> $ sudo intel_frequency --custom max=900
> $ sudo intel_frequency --get max
> cur: 300 MHz
> min: 300 MHz
> RP1: 200 MHz
> max: 900 MHz
> 
> $ sudo ./tools/intel_frequency --max
> $ sudo ./tools/intel_frequency -g
> cur: 1200 MHz
> min: 1200 MHz
> RP1: 200 MHz
> max: 1200 MHz
> 
> $ sudo intel_frequency -e
> $ sudo intel_frequency -g
> cur: 200 MHz
> min: 200 MHz
> RP1: 200 MHz
> max: 200 MHz
> 
> $ sudo intel_frequency --max
> $ sudo intel_frequency -g
> cur: 1200 MHz
> min: 1200 MHz
> RP1: 200 MHz
> max: 1200 MHz
> 
> $ sudo intel_frequency --min
> $ sudo intel_frequency -g
> cur: 200 MHz
> min: 200 MHz
> RP1: 200 MHz
> max: 200 MHz
> ---
>  tools/Makefile.sources  |   1 +
>  tools/intel_frequency.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 326 insertions(+)
>  create mode 100644 tools/intel_frequency.c
> 
> diff --git a/tools/Makefile.sources b/tools/Makefile.sources
> index b85a6b8..2bea389 100644
> --- a/tools/Makefile.sources
> +++ b/tools/Makefile.sources
> @@ -14,6 +14,7 @@ bin_PROGRAMS = 				\
>  	intel_dump_decode 		\
>  	intel_error_decode 		\
>  	intel_forcewaked		\
> +	intel_frequency			\
>  	intel_framebuffer_dump 		\
>  	intel_gpu_time 			\
>  	intel_gpu_top 			\
> diff --git a/tools/intel_frequency.c b/tools/intel_frequency.c
> new file mode 100644
> index 0000000..87b1741
> --- /dev/null
> +++ b/tools/intel_frequency.c
> @@ -0,0 +1,325 @@
> +/*
> + * Copyright © 2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +#define _GNU_SOURCE
> +#include <assert.h>
> +#include <getopt.h>
> +#include <stdio.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include "drmtest.h"
> +#include "intel_chipset.h"
> +
> +static int device, devid;
> +
> +enum {
> +	CUR=0,
> +	MIN,
> +	EFF,
> +	MAX,
> +	RP0,
> +	RPn
> +};
> +
> +struct freq_info {
> +	const char *name;
> +	const char *mode;
> +	FILE *filp;
> +	char *path;
> +};
> +
> +static struct freq_info info[] = {
> +	{ "cur", "r"  },
> +	{ "min", "rb+" },
> +	{ "RP1", "r" },
> +	{ "max", "rb+" },
> +	{ "RP0", "r" },
> +	{ "RPn", "r" }
> +};
> +
> +static char *
> +get_sysfs_path(const char *which)
> +{
> +	static const char fmt[] = "/sys/class/drm/card%1d/gt_%3s_freq_mhz";
> +	char *path;
> +	int ret;
> +
> +#define STATIC_STRLEN(string) (sizeof(string) / sizeof(string [0]))
> +	ret = asprintf(&path, fmt, device, which);
> +	assert(ret == (STATIC_STRLEN(fmt) - 3));
> +#undef STATIC_STRLEN
> +
> +	return path;
> +}
> +
> +static void
> +initialize_freq_info(struct freq_info *freq_info)
> +{
> +	if (freq_info->filp)
> +		return;
> +
> +	freq_info->path = get_sysfs_path(freq_info->name);
> +	assert(freq_info->path);
> +	freq_info->filp = fopen(freq_info->path, freq_info->mode);
> +	assert(freq_info->filp);
> +}
> +
> +static void wait_freq_settle(void)
> +{
> +	struct timespec ts;
> +
> +	/* FIXME: Lazy sleep without check. */
> +	ts.tv_sec = 0;
> +	ts.tv_nsec = 20000;
> +	clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
> +}
> +
> +static void set_frequency(struct freq_info *freq_info, int val)
> +{
> +	initialize_freq_info(freq_info);
> +	rewind(freq_info->filp);
> +	assert(fprintf(freq_info->filp, "%d", val) > 0);
> +
> +	wait_freq_settle();
> +}
> +
> +static int get_frequency(struct freq_info *freq_info)
> +{
> +	int val;
> +
> +	initialize_freq_info(freq_info);
> +	rewind(freq_info->filp);
> +	assert(fscanf(freq_info->filp, "%d", &val)==1);
> +
> +	return val;
> +}
> +
> +static void
> +usage(const char *prog)
> +{
> +	printf("Usage: %s [-ef] [--min | --max] [-g (min|max|efficient)][-s frequency_mhz]\n\n", prog);
> +	printf("%s A program to manipulate Intel GPU frequencies.\n\n", prog);
> +	printf("Options: \n");
> +	printf("  -e		Lock frequency to the most efficient frequency\n");
> +	printf("  -g, --get=    Get the frequency (optional arg: \"cur\"|\"min\"|\"max\"|\"eff\")\n");
> +	printf("  -s, --set     Lock frequency to an absolute value (MHz)\n");
> +	printf("  -c, --custom  Set a min, or max frequency \"min=X | max=Y\"\n");
> +	printf("  -m  --max     Lock frequency to max frequency\n");
> +	printf("      --min     Lock frequency to min (never a good idea, DEBUG ONLY)\n");
> +	printf("Examples:\n");
> +	printf("\tintel_frequency -gmin,cur Get the current and minimum frequency\n");
> +	printf("\tintel_frequency -s 400    Lock frequency to 400Mhz\n");
> +	exit(EXIT_FAILURE);
> +}
> +
> +/* Returns read or write operation */
> +static bool
> +parse(int argc, char *argv[], bool *act_upon, int *new_freq)
> +{
> +	int c, tmp;
> +	bool write = false;
> +
> +	char *token[] = {
> +		(char *)info[CUR].name,
> +		(char *)info[MIN].name,
> +		(char *)"eff",
> +		(char *)info[MAX].name
> +	};
> +
> +	/* No args means -g" */
> +	if (argc == 1) {
> +		for (c = 0; c < ARRAY_SIZE(act_upon); c++)
> +			act_upon[c] = true;
> +		goto done;
> +	}
> +	while (1) {
> +		int option_index = 0;
> +		static struct option long_options[] = {
> +			{ "get", optional_argument, NULL, 'g' },
> +			{ "set", required_argument, NULL, 's' },
> +			{ "custom", required_argument, NULL, 'c'},
> +			{ "min", no_argument, NULL, 'i' },
> +			{ "max", no_argument, NULL, 'm' },
> +			{ "help", no_argument, NULL, 'h' },
> +			{ NULL, 0, NULL, 0}
> +		};
> +
> +		c = getopt_long(argc, argv, "eg::s:c:mih", long_options, &option_index);
> +		if (c == -1)
> +			break;
> +
> +		switch (c) {
> +		case 'g':
> +			if (write == true)
> +				fprintf(stderr, "Read and write operations not support simultaneously.\n");
> +
> +			if (optarg) {
> +				char *value, *subopts = optarg;
> +				int x;
> +				while (*subopts != '\0') {
> +					x = getsubopt(&subopts, token, &value);
> +					if (x == -1) {
> +						fprintf(stderr, "Unrecognized option (%s)\n", value);
> +						break;
> +					} else
> +						act_upon[x] = true;
> +				}
> +			} else {
> +				int i;
> +				for (i = 0; i < ARRAY_SIZE(act_upon); i++)
> +					act_upon[i] = true;
> +			}
> +			break;
> +		case 's':
> +			if (!optarg)
> +				usage(argv[0]);
> +
> +			if (write == true) {
> +				fprintf(stderr, "Only one write may be specified at a time\n");
> +				exit(EXIT_FAILURE);
> +			}
> +
> +			write = true;
> +			act_upon[MIN] = true;
> +			act_upon[MAX] = true;
> +			sscanf(optarg, "%d", &new_freq[MAX]);
> +			new_freq[MIN] = new_freq[MAX];
> +			break;
> +		case 'c':
> +			if (!optarg)
> +				usage(argv[0]);
> +
> +			if (write == true) {
> +				fprintf(stderr, "Only one write may be specified at a time\n");
> +				exit(EXIT_FAILURE);
> +			}
> +
> +			write = true;
> +
> +			if (!strncmp("min=", optarg, 4)) {
> +				act_upon[MIN] = true;
> +				sscanf(optarg+4, "%d", &new_freq[MIN]);
> +			} else if (!strncmp("max=", optarg, 4)) {
> +				act_upon[MAX] = true;
> +				sscanf(optarg+4, "%d", &new_freq[MAX]);
> +			} else {
> +				fprintf(stderr, "Selected unmodifiable frequency\n");
> +				exit(EXIT_FAILURE);
> +			}
> +			break;
> +		case 'e':
> +			if (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid)) {
> +				/* the LP parts have special efficient frequencies */
> +				fprintf(stderr, "FIXME: Warning efficient frequency information is incorrect.\n");
> +				exit(EXIT_FAILURE);
> +			}
> +			tmp = get_frequency(&info[EFF]);
> +			new_freq[MIN] = tmp;
> +			new_freq[MAX] = tmp;
> +			act_upon[MIN] = true;
> +			act_upon[MAX] = true;
> +			write = true;
> +			break;
> +		case 'i': /* mIn */
> +			tmp = get_frequency(&info[RPn]);
> +			new_freq[MIN] = tmp;
> +			new_freq[MAX] = tmp;
> +			act_upon[MIN] = true;
> +			act_upon[MAX] = true;
> +			write = true;
> +			break;
> +		case 'm': /* max */
> +			tmp = get_frequency(&info[RP0]);
> +			new_freq[MIN] = tmp;
> +			new_freq[MAX] = tmp;
> +			act_upon[MIN] = true;
> +			act_upon[MAX] = true;
> +			write = true;
> +			break;
> +		case 'h':
> +		default:
> +			usage(argv[0]);
> +		}
> +	}
> +
> +done:
> +	return write;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +
> +	bool write, fail, targets[MAX+1] = {false};
> +	int i, try = 1, set_freq[MAX+1] = {0};
> +
> +	devid = intel_get_drm_devid(drm_open_any());
> +	device = drm_get_card();
> +
> +	write = parse(argc, argv, targets, set_freq);
> +	fail = write;
> +
> +	/* If we've previously locked the frequency, we need to make sure to set things
> +	 * in the correct order, or else the operation will fail (ie. min = max = 200,
> +	 * and we set min to 300, we fail because it would try to set min >
> +	 * max). This can be accomplished be going either forward or reverse
> +	 * through the loop. MIN is always before MAX.
> +	 *
> +	 * XXX: Since only min and max are at play, the super lazy way is to do this
> +	 * 3 times and if we still fail after 3, it's for real.
> +	 */
> +again:
> +	if (try > 2) {
> +		fprintf(stderr, "Did not achieve desired freq.\n");
> +		exit(EXIT_FAILURE);
> +	}
> +	for (i = 0; i < ARRAY_SIZE(targets); i++) {
> +		if (targets[i] == false)
> +			continue;
> +
> +		if (write) {
> +			set_frequency(&info[i], set_freq[i]);
> +			if (get_frequency(&info[i]) != set_freq[i])
> +				fail = true;
> +			else
> +				fail = false;
> +		} else {
> +			printf("%s: %d MHz\n", info[i].name, get_frequency(&info[i]));
> +		}
> +	}
> +
> +	if (fail) {
> +		try++;
> +		goto again;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(targets); i++) {
> +		if (info[i].filp) {
> +			fclose(info[i].filp);
> +			free(info[i].path);
> +		}
> +	}
> +
> +	return EXIT_SUCCESS;
> +}
> -- 
> 2.2.1
>
Jordan Justen Jan. 11, 2015, 5:54 p.m. UTC | #2
On 2015-01-10 20:19:29, Ben Widawsky wrote:
> +       printf("Usage: %s [-ef] [--min | --max] [-g (min|max|efficient)][-s frequency_mhz]\n\n", prog);

-ef => -f

Add space before [-s

> +       printf("%s A program to manipulate Intel GPU frequencies.\n\n", prog);
> +       printf("Options: \n");
> +       printf("  -e            Lock frequency to the most efficient frequency\n");
> +       printf("  -g, --get=    Get the frequency (optional arg: \"cur\"|\"min\"|\"max\"|\"eff\")\n");
> +       printf("  -s, --set     Lock frequency to an absolute value (MHz)\n");
> +       printf("  -c, --custom  Set a min, or max frequency \"min=X | max=Y\"\n");
> +       printf("  -m  --max     Lock frequency to max frequency\n");
> +       printf("      --min     Lock frequency to min (never a good idea, DEBUG ONLY)\n");
> +       printf("Examples:\n");
> +       printf("    intel_frequency -gmin,cur Get the current and minimum frequency\n");
> +       printf("    intel_frequency -s 400    Lock frequency to 400Mhz\n");

Add -c example?

Series Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
diff mbox

Patch

diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index b85a6b8..2bea389 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -14,6 +14,7 @@  bin_PROGRAMS = 				\
 	intel_dump_decode 		\
 	intel_error_decode 		\
 	intel_forcewaked		\
+	intel_frequency			\
 	intel_framebuffer_dump 		\
 	intel_gpu_time 			\
 	intel_gpu_top 			\
diff --git a/tools/intel_frequency.c b/tools/intel_frequency.c
new file mode 100644
index 0000000..87b1741
--- /dev/null
+++ b/tools/intel_frequency.c
@@ -0,0 +1,325 @@ 
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "drmtest.h"
+#include "intel_chipset.h"
+
+static int device, devid;
+
+enum {
+	CUR=0,
+	MIN,
+	EFF,
+	MAX,
+	RP0,
+	RPn
+};
+
+struct freq_info {
+	const char *name;
+	const char *mode;
+	FILE *filp;
+	char *path;
+};
+
+static struct freq_info info[] = {
+	{ "cur", "r"  },
+	{ "min", "rb+" },
+	{ "RP1", "r" },
+	{ "max", "rb+" },
+	{ "RP0", "r" },
+	{ "RPn", "r" }
+};
+
+static char *
+get_sysfs_path(const char *which)
+{
+	static const char fmt[] = "/sys/class/drm/card%1d/gt_%3s_freq_mhz";
+	char *path;
+	int ret;
+
+#define STATIC_STRLEN(string) (sizeof(string) / sizeof(string [0]))
+	ret = asprintf(&path, fmt, device, which);
+	assert(ret == (STATIC_STRLEN(fmt) - 3));
+#undef STATIC_STRLEN
+
+	return path;
+}
+
+static void
+initialize_freq_info(struct freq_info *freq_info)
+{
+	if (freq_info->filp)
+		return;
+
+	freq_info->path = get_sysfs_path(freq_info->name);
+	assert(freq_info->path);
+	freq_info->filp = fopen(freq_info->path, freq_info->mode);
+	assert(freq_info->filp);
+}
+
+static void wait_freq_settle(void)
+{
+	struct timespec ts;
+
+	/* FIXME: Lazy sleep without check. */
+	ts.tv_sec = 0;
+	ts.tv_nsec = 20000;
+	clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+}
+
+static void set_frequency(struct freq_info *freq_info, int val)
+{
+	initialize_freq_info(freq_info);
+	rewind(freq_info->filp);
+	assert(fprintf(freq_info->filp, "%d", val) > 0);
+
+	wait_freq_settle();
+}
+
+static int get_frequency(struct freq_info *freq_info)
+{
+	int val;
+
+	initialize_freq_info(freq_info);
+	rewind(freq_info->filp);
+	assert(fscanf(freq_info->filp, "%d", &val)==1);
+
+	return val;
+}
+
+static void
+usage(const char *prog)
+{
+	printf("Usage: %s [-ef] [--min | --max] [-g (min|max|efficient)][-s frequency_mhz]\n\n", prog);
+	printf("%s A program to manipulate Intel GPU frequencies.\n\n", prog);
+	printf("Options: \n");
+	printf("  -e		Lock frequency to the most efficient frequency\n");
+	printf("  -g, --get=    Get the frequency (optional arg: \"cur\"|\"min\"|\"max\"|\"eff\")\n");
+	printf("  -s, --set     Lock frequency to an absolute value (MHz)\n");
+	printf("  -c, --custom  Set a min, or max frequency \"min=X | max=Y\"\n");
+	printf("  -m  --max     Lock frequency to max frequency\n");
+	printf("      --min     Lock frequency to min (never a good idea, DEBUG ONLY)\n");
+	printf("Examples:\n");
+	printf("\tintel_frequency -gmin,cur Get the current and minimum frequency\n");
+	printf("\tintel_frequency -s 400    Lock frequency to 400Mhz\n");
+	exit(EXIT_FAILURE);
+}
+
+/* Returns read or write operation */
+static bool
+parse(int argc, char *argv[], bool *act_upon, int *new_freq)
+{
+	int c, tmp;
+	bool write = false;
+
+	char *token[] = {
+		(char *)info[CUR].name,
+		(char *)info[MIN].name,
+		(char *)"eff",
+		(char *)info[MAX].name
+	};
+
+	/* No args means -g" */
+	if (argc == 1) {
+		for (c = 0; c < ARRAY_SIZE(act_upon); c++)
+			act_upon[c] = true;
+		goto done;
+	}
+	while (1) {
+		int option_index = 0;
+		static struct option long_options[] = {
+			{ "get", optional_argument, NULL, 'g' },
+			{ "set", required_argument, NULL, 's' },
+			{ "custom", required_argument, NULL, 'c'},
+			{ "min", no_argument, NULL, 'i' },
+			{ "max", no_argument, NULL, 'm' },
+			{ "help", no_argument, NULL, 'h' },
+			{ NULL, 0, NULL, 0}
+		};
+
+		c = getopt_long(argc, argv, "eg::s:c:mih", long_options, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'g':
+			if (write == true)
+				fprintf(stderr, "Read and write operations not support simultaneously.\n");
+
+			if (optarg) {
+				char *value, *subopts = optarg;
+				int x;
+				while (*subopts != '\0') {
+					x = getsubopt(&subopts, token, &value);
+					if (x == -1) {
+						fprintf(stderr, "Unrecognized option (%s)\n", value);
+						break;
+					} else
+						act_upon[x] = true;
+				}
+			} else {
+				int i;
+				for (i = 0; i < ARRAY_SIZE(act_upon); i++)
+					act_upon[i] = true;
+			}
+			break;
+		case 's':
+			if (!optarg)
+				usage(argv[0]);
+
+			if (write == true) {
+				fprintf(stderr, "Only one write may be specified at a time\n");
+				exit(EXIT_FAILURE);
+			}
+
+			write = true;
+			act_upon[MIN] = true;
+			act_upon[MAX] = true;
+			sscanf(optarg, "%d", &new_freq[MAX]);
+			new_freq[MIN] = new_freq[MAX];
+			break;
+		case 'c':
+			if (!optarg)
+				usage(argv[0]);
+
+			if (write == true) {
+				fprintf(stderr, "Only one write may be specified at a time\n");
+				exit(EXIT_FAILURE);
+			}
+
+			write = true;
+
+			if (!strncmp("min=", optarg, 4)) {
+				act_upon[MIN] = true;
+				sscanf(optarg+4, "%d", &new_freq[MIN]);
+			} else if (!strncmp("max=", optarg, 4)) {
+				act_upon[MAX] = true;
+				sscanf(optarg+4, "%d", &new_freq[MAX]);
+			} else {
+				fprintf(stderr, "Selected unmodifiable frequency\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+		case 'e':
+			if (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid)) {
+				/* the LP parts have special efficient frequencies */
+				fprintf(stderr, "FIXME: Warning efficient frequency information is incorrect.\n");
+				exit(EXIT_FAILURE);
+			}
+			tmp = get_frequency(&info[EFF]);
+			new_freq[MIN] = tmp;
+			new_freq[MAX] = tmp;
+			act_upon[MIN] = true;
+			act_upon[MAX] = true;
+			write = true;
+			break;
+		case 'i': /* mIn */
+			tmp = get_frequency(&info[RPn]);
+			new_freq[MIN] = tmp;
+			new_freq[MAX] = tmp;
+			act_upon[MIN] = true;
+			act_upon[MAX] = true;
+			write = true;
+			break;
+		case 'm': /* max */
+			tmp = get_frequency(&info[RP0]);
+			new_freq[MIN] = tmp;
+			new_freq[MAX] = tmp;
+			act_upon[MIN] = true;
+			act_upon[MAX] = true;
+			write = true;
+			break;
+		case 'h':
+		default:
+			usage(argv[0]);
+		}
+	}
+
+done:
+	return write;
+}
+
+int main(int argc, char *argv[])
+{
+
+	bool write, fail, targets[MAX+1] = {false};
+	int i, try = 1, set_freq[MAX+1] = {0};
+
+	devid = intel_get_drm_devid(drm_open_any());
+	device = drm_get_card();
+
+	write = parse(argc, argv, targets, set_freq);
+	fail = write;
+
+	/* If we've previously locked the frequency, we need to make sure to set things
+	 * in the correct order, or else the operation will fail (ie. min = max = 200,
+	 * and we set min to 300, we fail because it would try to set min >
+	 * max). This can be accomplished be going either forward or reverse
+	 * through the loop. MIN is always before MAX.
+	 *
+	 * XXX: Since only min and max are at play, the super lazy way is to do this
+	 * 3 times and if we still fail after 3, it's for real.
+	 */
+again:
+	if (try > 2) {
+		fprintf(stderr, "Did not achieve desired freq.\n");
+		exit(EXIT_FAILURE);
+	}
+	for (i = 0; i < ARRAY_SIZE(targets); i++) {
+		if (targets[i] == false)
+			continue;
+
+		if (write) {
+			set_frequency(&info[i], set_freq[i]);
+			if (get_frequency(&info[i]) != set_freq[i])
+				fail = true;
+			else
+				fail = false;
+		} else {
+			printf("%s: %d MHz\n", info[i].name, get_frequency(&info[i]));
+		}
+	}
+
+	if (fail) {
+		try++;
+		goto again;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(targets); i++) {
+		if (info[i].filp) {
+			fclose(info[i].filp);
+			free(info[i].path);
+		}
+	}
+
+	return EXIT_SUCCESS;
+}