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@xxxxxxxxxxxx> > > 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 > _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx