1 /*
2 * Copyright © 2015,2018 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <getopt.h>
26 #include <stdio.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include "drmtest.h"
31 #include "igt_device.h"
32 #include "intel_chipset.h"
33
34 #define VERSION "1.0"
35
36 static int device, devid;
37
38 enum {
39 CUR=0,
40 MIN,
41 EFF,
42 MAX,
43 RP0,
44 RPn
45 };
46
47 struct freq_info {
48 const char *name;
49 const char *mode;
50 FILE *filp;
51 char *path;
52 };
53
54 static struct freq_info info[] = {
55 { "cur", "r" },
56 { "min", "rb+" },
57 { "RP1", "r" },
58 { "max", "rb+" },
59 { "RP0", "r" },
60 { "RPn", "r" }
61 };
62
63 static char *
get_sysfs_path(const char * which)64 get_sysfs_path(const char *which)
65 {
66 static const char fmt[] = "/sys/class/drm/card%1d/gt_%3s_freq_mhz";
67 char *path;
68 int ret;
69
70 #define STATIC_STRLEN(string) (sizeof(string) / sizeof(string [0]))
71 ret = asprintf(&path, fmt, device, which);
72 assert(ret == (STATIC_STRLEN(fmt) - 3));
73 #undef STATIC_STRLEN
74
75 return path;
76 }
77
78 static void
initialize_freq_info(struct freq_info * freq_info)79 initialize_freq_info(struct freq_info *freq_info)
80 {
81 if (freq_info->filp)
82 return;
83
84 freq_info->path = get_sysfs_path(freq_info->name);
85 assert(freq_info->path);
86 freq_info->filp = fopen(freq_info->path, freq_info->mode);
87 assert(freq_info->filp);
88 }
89
wait_freq_settle(void)90 static void wait_freq_settle(void)
91 {
92 struct timespec ts;
93
94 /* FIXME: Lazy sleep without check. */
95 ts.tv_sec = 0;
96 ts.tv_nsec = 20000;
97 clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
98 }
99
set_frequency(struct freq_info * freq_info,int val)100 static void set_frequency(struct freq_info *freq_info, int val)
101 {
102 initialize_freq_info(freq_info);
103 rewind(freq_info->filp);
104 assert(fprintf(freq_info->filp, "%d", val) > 0);
105
106 wait_freq_settle();
107 }
108
get_frequency(struct freq_info * freq_info)109 static int get_frequency(struct freq_info *freq_info)
110 {
111 int val;
112
113 initialize_freq_info(freq_info);
114 rewind(freq_info->filp);
115 assert(fscanf(freq_info->filp, "%d", &val)==1);
116
117 return val;
118 }
119
120 static void __attribute__((noreturn))
usage(const char * prog)121 usage(const char *prog)
122 {
123 printf("%s A program to manipulate Intel GPU frequencies.\n\n", prog);
124 printf("Usage: %s [-e] [--min | --max] [--get] [--set frequency_mhz]\n\n", prog);
125 printf("Options: \n");
126 printf(" -e Lock frequency to the most efficient frequency\n");
127 printf(" -g, --get Get all the frequency settings\n");
128 printf(" -s, --set Lock frequency to an absolute value (MHz)\n");
129 printf(" -c, --custom Set a min, or max frequency \"min=X | max=Y\"\n");
130 printf(" -m --max Lock frequency to max frequency\n");
131 printf(" -i --min Lock frequency to min (never a good idea, DEBUG ONLY)\n");
132 printf(" -d --defaults Return the system to hardware defaults\n");
133 printf(" -h --help Returns this\n");
134 printf(" -v --version Version\n");
135 printf("\n");
136 printf("Examples:\n");
137 printf(" intel_gpu_frequency --get\t\tGet the current and minimum frequency\n");
138 printf(" intel_gpu_frequency --set 400\tLock frequency to 400Mhz\n");
139 printf(" intel_gpu_frequency --custom max=750\tSet the max frequency to 750MHz\n");
140 printf("\n");
141 printf("Report bugs to <bugs.freedesktop.org>\n");
142 exit(EXIT_FAILURE);
143 }
144
145 static void
version(const char * prog)146 version(const char *prog)
147 {
148 printf("%s: %s\n", prog, VERSION);
149 printf("Copyright © 2015,2018 Intel Corporation\n");
150 }
151
152 /* Returns read or write operation */
153 static bool
parse(int argc,char * argv[],bool * act_upon,size_t act_upon_n,int * new_freq)154 parse(int argc, char *argv[], bool *act_upon, size_t act_upon_n, int *new_freq)
155 {
156 int c, tmp;
157 bool write = false;
158
159 /* No args means -g" */
160 if (argc == 1) {
161 for (c = 0; c < act_upon_n; c++)
162 act_upon[c] = true;
163 goto done;
164 }
165 while (1) {
166 int option_index = 0;
167 static struct option long_options[] = {
168 { "get", no_argument, NULL, 'g' },
169 { "set", required_argument, NULL, 's' },
170 { "custom", required_argument, NULL, 'c'},
171 { "min", no_argument, NULL, 'i' },
172 { "max", no_argument, NULL, 'm' },
173 { "defaults", no_argument, NULL, 'd' },
174 { "help", no_argument, NULL, 'h' },
175 { "version", no_argument, NULL, 'v' },
176 { NULL, 0, NULL, 0}
177 };
178
179 c = getopt_long(argc, argv, "egs:c:midh", long_options, &option_index);
180 if (c == -1)
181 break;
182
183 switch (c) {
184 case 'g':
185 if (write == true)
186 fprintf(stderr, "Read and write operations not support simultaneously.\n");
187 {
188 int i;
189 for (i = 0; i < act_upon_n; i++)
190 act_upon[i] = true;
191 }
192 break;
193 case 's':
194 if (!optarg)
195 usage(argv[0]);
196
197 if (write == true) {
198 fprintf(stderr, "Only one write may be specified at a time\n");
199 exit(EXIT_FAILURE);
200 }
201
202 write = true;
203 act_upon[MIN] = true;
204 act_upon[MAX] = true;
205 sscanf(optarg, "%d", &new_freq[MAX]);
206 new_freq[MIN] = new_freq[MAX];
207 break;
208 case 'c':
209 if (!optarg)
210 usage(argv[0]);
211
212 if (write == true) {
213 fprintf(stderr, "Only one write may be specified at a time\n");
214 exit(EXIT_FAILURE);
215 }
216
217 write = true;
218
219 if (!strncmp("min=", optarg, 4)) {
220 act_upon[MIN] = true;
221 sscanf(optarg+4, "%d", &new_freq[MIN]);
222 } else if (!strncmp("max=", optarg, 4)) {
223 act_upon[MAX] = true;
224 sscanf(optarg+4, "%d", &new_freq[MAX]);
225 } else {
226 fprintf(stderr, "Selected unmodifiable frequency\n");
227 exit(EXIT_FAILURE);
228 }
229 break;
230 case 'e': /* efficient */
231 if (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid)) {
232 /* the LP parts have special efficient frequencies */
233 fprintf(stderr,
234 "FIXME: Warning efficient frequency information is incorrect.\n");
235 exit(EXIT_FAILURE);
236 }
237 tmp = get_frequency(&info[EFF]);
238 new_freq[MIN] = tmp;
239 new_freq[MAX] = tmp;
240 act_upon[MIN] = true;
241 act_upon[MAX] = true;
242 write = true;
243 break;
244 case 'i': /* mIn */
245 tmp = get_frequency(&info[RPn]);
246 new_freq[MIN] = tmp;
247 new_freq[MAX] = tmp;
248 act_upon[MIN] = true;
249 act_upon[MAX] = true;
250 write = true;
251 break;
252 case 'm': /* max */
253 tmp = get_frequency(&info[RP0]);
254 new_freq[MIN] = tmp;
255 new_freq[MAX] = tmp;
256 act_upon[MIN] = true;
257 act_upon[MAX] = true;
258 write = true;
259 break;
260 case 'd': /* defaults */
261 new_freq[MIN] = get_frequency(&info[RPn]);
262 new_freq[MAX] = get_frequency(&info[RP0]);
263 act_upon[MIN] = true;
264 act_upon[MAX] = true;
265 write = true;
266 break;
267 case 'v':
268 version(argv[0]);
269 exit(0);
270 case 'h':
271 default:
272 usage(argv[0]);
273 }
274 }
275
276 done:
277 return write;
278 }
279
main(int argc,char * argv[])280 int main(int argc, char *argv[])
281 {
282
283 bool write, fail, targets[MAX+1] = {false};
284 int i, fd, try = 1, set_freq[MAX+1] = {0};
285
286 fd = drm_open_driver(DRIVER_INTEL);
287 devid = intel_get_drm_devid(fd);
288 device = igt_device_get_card_index(fd);
289 close(fd);
290
291 write = parse(argc, argv, targets, ARRAY_SIZE(targets), set_freq);
292 fail = write;
293
294 /* If we've previously locked the frequency, we need to make sure to set things
295 * in the correct order, or else the operation will fail (ie. min = max = 200,
296 * and we set min to 300, we fail because it would try to set min >
297 * max). This can be accomplished be going either forward or reverse
298 * through the loop. MIN is always before MAX.
299 *
300 * XXX: Since only min and max are at play, the super lazy way is to do this
301 * 3 times and if we still fail after 3, it's for real.
302 */
303 again:
304 if (try > 2) {
305 fprintf(stderr, "Did not achieve desired freq.\n");
306 exit(EXIT_FAILURE);
307 }
308 for (i = 0; i < ARRAY_SIZE(targets); i++) {
309 if (targets[i] == false)
310 continue;
311
312 if (write) {
313 set_frequency(&info[i], set_freq[i]);
314 if (get_frequency(&info[i]) != set_freq[i])
315 fail = true;
316 else
317 fail = false;
318 } else {
319 printf("%s: %d MHz\n", info[i].name, get_frequency(&info[i]));
320 }
321 }
322
323 if (fail) {
324 try++;
325 goto again;
326 }
327
328 for (i = 0; i < ARRAY_SIZE(targets); i++) {
329 if (info[i].filp) {
330 fclose(info[i].filp);
331 free(info[i].path);
332 }
333 }
334
335 return EXIT_SUCCESS;
336 }
337