xref: /aosp_15_r20/external/igt-gpu-tools/tools/intel_gpu_frequency.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
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