summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--man/Makefile.am1
-rw-r--r--man/intel_gpu_frequency.man58
-rw-r--r--tools/Makefile.sources1
-rw-r--r--tools/intel_gpu_frequency.c381
5 files changed, 443 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index 143c361a..67146a3c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
Release 1.10 (XXXX-XX-XX)
-------------------------
+- New frequency manipulation tool (intel_gpu_frequency)
+
Release 1.9 (2014-12-12)
------------------------
diff --git a/man/Makefile.am b/man/Makefile.am
index 0d04f934..dcd79528 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -4,6 +4,7 @@ appman_PRE = \
intel_bios_dumper.man \
intel_bios_reader.man \
intel_error_decode.man \
+ intel_gpu_frequency.man \
intel_gpu_top.man \
intel_gtt.man \
intel_infoframes.man \
diff --git a/man/intel_gpu_frequency.man b/man/intel_gpu_frequency.man
new file mode 100644
index 00000000..60e4e0c8
--- /dev/null
+++ b/man/intel_gpu_frequency.man
@@ -0,0 +1,58 @@
+.TH INTEL_FREQUENCY: "1" "January 2015" "intel_gpu_frequency" "User Commands"
+.SH NAME
+intel_gpu_frequency: \- manual page for intel_gpu_frequency
+.SH SYNOPSIS
+.B intel_gpu_frequency
+[\fI\,-e\/\fR] [\fI\,--min | --max\/\fR] [\fI\,-g (min|max|efficient)\/\fR] [\fI\,-s frequency_mhz\/\fR]
+.SH DESCRIPTION
+\&A program to manipulate Intel GPU frequencies. Intel GPUs
+will automatically throttle the frequencies based on system demands, up when
+needed, down when not. This tool should only be used for debugging performance
+problems, or trying to get a stable frequency while benchmarking.
+
+Intel GPUs only accept specific frequencies. The tool may, or may not attempt to
+adjust requests to the proper frequency if they aren't correct. This may lead to
+non-obvious failures when setting frequency. Multiples of 50MHz is usually a
+safe bet.
+.SH OPTIONS
+.TP
+\fB\-e\fR
+Lock frequency to the most efficient frequency
+.TP
+\fB\-g\fR, \fB\-\-get=\fR
+Get the frequency comma separated list of ("cur"|"min"|"max"|"eff")
+.TP
+\fB\-s\fR, \fB\-\-set\fR
+Lock frequency to an absolute value (MHz)
+.TP
+\fB\-c\fR, \fB\-\-custom\fR
+Set a min, or max frequency "min=X | max=Y"
+.TP
+\fB\-m\fR \fB\-\-max\fR
+Lock frequency to max frequency
+.TP
+\fB\-i\fR \fB\-\-min\fR
+Lock frequency to min (never a good idea, DEBUG ONLY)
+.TP
+\fB\-d\fR \fB\-\-defaults\fR
+Return the system to hardware defaults
+.TP
+\fB\-h\fR \fB\-\-help\fR
+Returns this
+.HP
+\fB\-v\fR \fB\-\-version\fR Version
+.SH EXAMPLES
+.TP
+\fbintel_gpu_frequency \-gmin,cur\fR
+Get the current and minimum frequency
+.TP
+\fbintel_gpu_frequency \-s 400\fR
+Lock frequency to 400Mhz
+.TP
+\fbintel_gpu_frequency \-c max=750\fR
+Set the max frequency to 750MHz
+.PP
+.SH "REPORTING BUGS"
+Report bugs to https://bugs.freedesktop.org
+.SH COPYRIGHT
+Copyright (C) 2015 Intel Corporation
diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index b85a6b88..b16af8a6 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -14,6 +14,7 @@ bin_PROGRAMS = \
intel_dump_decode \
intel_error_decode \
intel_forcewaked \
+ intel_gpu_frequency \
intel_framebuffer_dump \
intel_gpu_time \
intel_gpu_top \
diff --git a/tools/intel_gpu_frequency.c b/tools/intel_gpu_frequency.c
new file mode 100644
index 00000000..d9e7d228
--- /dev/null
+++ b/tools/intel_gpu_frequency.c
@@ -0,0 +1,381 @@
+/*
+ * 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.
+ *
+ * Example:
+ * Get all frequencies:
+ * intel_gpu_frequency --get=cur,min,max,eff
+ *
+ * Same as above:
+ * intel_gpu_frequency -g
+ *
+ * Get the efficient frequency:
+ * intel_gpu_frequency -geff
+ *
+ * Lock the GPU frequency to 300MHz:
+ * intel_gpu_frequency --set min=300
+ *
+ * Set the maximum frequency to 900MHz:
+ * intel_gpu_frequency --custom max=900
+ *
+ * Lock the GPU frequency to its maximum frequency:
+ * intel_gpu_frequency --max
+ *
+ * Lock the GPU frequency to its most efficient frequency:
+ * intel_gpu_frequency -e
+ *
+ * Lock The GPU frequency to its minimum frequency:
+ * intel_gpu_frequency --min
+ *
+ * Reset the GPU to hardware defaults
+ * intel_gpu_frequency -d
+ */
+
+#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"
+
+#define VERSION "1.0"
+
+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("%s A program to manipulate Intel GPU frequencies.\n\n", prog);
+ printf("Usage: %s [-e] [--min | --max] [-g (min|max|efficient)] [-s frequency_mhz]\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(" -i --min Lock frequency to min (never a good idea, DEBUG ONLY)\n");
+ printf(" -d --defaults Return the system to hardware defaults\n");
+ printf(" -h --help Returns this\n");
+ printf(" -v --version Version\n");
+ printf("\n");
+ printf("Examples:\n");
+ printf(" intel_gpu_frequency -gmin,cur\tGet the current and minimum frequency\n");
+ printf(" intel_gpu_frequency -s 400\tLock frequency to 400Mhz\n");
+ printf(" intel_gpu_frequency -c max=750\tSet the max frequency to 750MHz\n");
+ printf("\n");
+ printf("Report bugs to <bugs.freedesktop.org>\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+version(const char *prog)
+{
+ printf("%s: %s\n", prog, VERSION);
+ printf("Copyright © 2015 Intel Corporation\n");
+}
+
+/* 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' },
+ { "defaults", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0}
+ };
+
+ c = getopt_long(argc, argv, "eg::s:c:midh", 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': /* efficient */
+ 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 'd': /* defaults */
+ new_freq[MIN] = get_frequency(&info[RPn]);
+ new_freq[MAX] = get_frequency(&info[RP0]);
+ act_upon[MIN] = true;
+ act_upon[MAX] = true;
+ write = true;
+ break;
+ case 'v':
+ version(argv[0]);
+ exit(0);
+ 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;
+}