/* * Copyright © 2015,2018 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. */ #include #include #include #include #include #include "drmtest.h" #include "igt_device.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 __attribute__((noreturn)) usage(const char *prog) { printf("%s A program to manipulate Intel GPU frequencies.\n\n", prog); printf("Usage: %s [-e] [--min | --max] [--get] [--set frequency_mhz]\n\n", prog); printf("Options: \n"); printf(" -e Lock frequency to the most efficient frequency\n"); printf(" -g, --get Get all the frequency settings\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 --get\t\tGet the current and minimum frequency\n"); printf(" intel_gpu_frequency --set 400\tLock frequency to 400Mhz\n"); printf(" intel_gpu_frequency --custom max=750\tSet the max frequency to 750MHz\n"); printf("\n"); printf("Report bugs to \n"); exit(EXIT_FAILURE); } static void version(const char *prog) { printf("%s: %s\n", prog, VERSION); printf("Copyright © 2015,2018 Intel Corporation\n"); } /* Returns read or write operation */ static bool parse(int argc, char *argv[], bool *act_upon, size_t act_upon_n, int *new_freq) { int c, tmp; bool write = false; /* No args means -g" */ if (argc == 1) { for (c = 0; c < act_upon_n; c++) act_upon[c] = true; goto done; } while (1) { int option_index = 0; static struct option long_options[] = { { "get", no_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, "egs: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"); { int i; for (i = 0; i < act_upon_n; 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, fd, try = 1, set_freq[MAX+1] = {0}; fd = drm_open_driver(DRIVER_INTEL); devid = intel_get_drm_devid(fd); device = igt_device_get_card_index(fd); close(fd); write = parse(argc, argv, targets, ARRAY_SIZE(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; }