1 /*
2  * Copyright © 2013 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 DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <stdio.h>
30 
31 #include "igt_perf.h"
32 
33 #include "gpu-freq.h"
34 #include "debugfs.h"
35 
perf_open(void)36 static int perf_open(void)
37 {
38 	int fd;
39 
40 	fd = perf_i915_open_group(I915_PMU_ACTUAL_FREQUENCY, -1);
41 	if (perf_i915_open_group(I915_PMU_REQUESTED_FREQUENCY, fd) < 0) {
42 		close(fd);
43 		fd = -1;
44 	}
45 
46 	return fd;
47 }
48 
gpu_freq_init(struct gpu_freq * gf)49 int gpu_freq_init(struct gpu_freq *gf)
50 {
51 	char buf[4096], *s;
52 	int fd, len = -1;
53 
54 	memset(gf, 0, sizeof(*gf));
55 
56 	gf->fd = perf_open();
57 
58 	sprintf(buf, "%s/i915_frequency_info", debugfs_dri_path);
59 	fd = open(buf, 0);
60 	if (fd < 0) {
61 		sprintf(buf, "%s/i915_cur_delayinfo", debugfs_dri_path);
62 		fd = open(buf, 0);
63 	}
64 	if (fd < 0)
65 		return gf->error = errno;
66 
67 	len = read(fd, buf, sizeof(buf)-1);
68 	close(fd);
69 	if (len < 0)
70 		goto err;
71 
72 	buf[len] = '\0';
73 
74 	if (strstr(buf, "PUNIT_REG_GPU_FREQ_STS")) {
75 		/* Baytrail is special, ofc. */
76 		gf->is_byt = 1;
77 
78 		s = strstr(buf, "max");
79 		if (s != NULL)
80 			sscanf(s, "max GPU freq: %d MHz", &gf->max);
81 
82 		s = strstr(buf, "min");
83 		if (s != NULL)
84 			sscanf(s, "min GPU freq: %d MHz", &gf->min);
85 
86 		gf->rp0 = gf->rp1 = gf->max;
87 		gf->rpn = gf->min;
88 	} else {
89 		s = strstr(buf, "(RPN)");
90 		if (s == NULL)
91 			goto err;
92 		sscanf(s, "(RPN) frequency: %dMHz", &gf->rpn);
93 
94 		s = strstr(s, "(RP1)");
95 		if (s == NULL)
96 			goto err;
97 		sscanf(s, "(RP1) frequency: %dMHz", &gf->rp1);
98 
99 		s = strstr(s, "(RP0)");
100 		if (s == NULL)
101 			goto err;
102 		sscanf(s, "(RP0) frequency: %dMHz", &gf->rp0);
103 
104 		s = strstr(s, "Max");
105 		if (s == NULL)
106 			goto err;
107 		sscanf(s, "Max overclocked frequency: %dMHz", &gf->max);
108 		gf->min = gf->rpn;
109 	}
110 
111 	return 0;
112 
113 err:
114 	return gf->error = EIO;
115 }
116 
gpu_freq_update(struct gpu_freq * gf)117 int gpu_freq_update(struct gpu_freq *gf)
118 {
119 	if (gf->error)
120 		return gf->error;
121 
122 	if (gf->fd < 0) {
123 		char buf[4096], *s;
124 		int fd, len = -1;
125 
126 		sprintf(buf, "%s/i915_frequency_info", debugfs_dri_path);
127 		fd = open(buf, 0);
128 		if (fd < 0) {
129 			sprintf(buf, "%s/i915_cur_delayinfo", debugfs_dri_path);
130 			fd = open(buf, 0);
131 		}
132 		if (fd < 0)
133 			return gf->error = errno;
134 
135 		len = read(fd, buf, sizeof(buf)-1);
136 		close(fd);
137 		if (len < 0)
138 			return gf->error = EIO;
139 
140 		buf[len] = '\0';
141 
142 		if (gf->is_byt) {
143 			s = strstr(buf, "current");
144 			if (s)
145 				sscanf(s, "current GPU freq: %d MHz", &gf->current);
146 			gf->request = gf->current;
147 		} else {
148 			s = strstr(buf, "RPNSWREQ:");
149 			if (s)
150 				sscanf(s, "RPNSWREQ: %dMHz", &gf->request);
151 
152 			s = strstr(buf, "CAGF:");
153 			if (s)
154 				sscanf(s, "CAGF: %dMHz", &gf->current);
155 		}
156 	} else {
157 		struct gpu_freq_stat *s = &gf->stat[gf->count++&1];
158 		struct gpu_freq_stat *d = &gf->stat[gf->count&1];
159 		uint64_t data[4], d_time;
160 		int len;
161 
162 		len = read(gf->fd, data, sizeof(data));
163 		if (len < 0)
164 			return gf->error = errno;
165 
166 		s->timestamp = data[1];
167 		s->act = data[2];
168 		s->req = data[3];
169 
170 		if (gf->count == 1)
171 			return EAGAIN;
172 
173 		d_time = s->timestamp - d->timestamp;
174 		if (d_time == 0) {
175 			gf->count--;
176 			return EAGAIN;
177 		}
178 
179 		gf->current = (s->act - d->act) * 1e9 / d_time;
180 		gf->request = (s->req - d->req) * 1e9 / d_time;
181 	}
182 
183 	return 0;
184 }
185