• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2021 Google, Inc.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <fcntl.h>
26 #include <ftw.h>
27 #include <inttypes.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <arpa/inet.h>
34 #include <sys/mman.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 
38 #include "util/macros.h"
39 #include "util/os_file.h"
40 
41 #include "freedreno_dt.h"
42 
43 static struct {
44    char *dtnode;
45    int address_cells, size_cells;
46    uint64_t base;
47    uint32_t size;
48    uint32_t min_freq;
49    uint32_t max_freq;
50 } dev;
51 
52 /*
53  * code to find stuff in /proc/device-tree:
54  *
55  * NOTE: if we sampled the counters from the cmdstream, we could avoid needing
56  * /dev/mem and /proc/device-tree crawling.  OTOH when the GPU is heavily loaded
57  * we would be competing with whatever else is using the GPU.
58  */
59 
60 static void *
readdt(const char * node)61 readdt(const char *node)
62 {
63    char *path;
64    void *buf;
65    size_t sz;
66 
67    (void)asprintf(&path, "%s/%s", dev.dtnode, node);
68    buf = os_read_file(path, &sz);
69    free(path);
70 
71    return buf;
72 }
73 
74 static int
find_freqs_fn(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)75 find_freqs_fn(const char *fpath, const struct stat *sb, int typeflag,
76               struct FTW *ftwbuf)
77 {
78    const char *fname = fpath + ftwbuf->base;
79    size_t sz;
80 
81    if (strcmp(fname, "qcom,gpu-freq") == 0) {
82       uint32_t *buf = (uint32_t *)os_read_file(fpath, &sz);
83       uint32_t freq = ntohl(buf[0]);
84       free(buf);
85       dev.max_freq = MAX2(dev.max_freq, freq);
86       dev.min_freq = MIN2(dev.min_freq, freq);
87    }
88 
89    return 0;
90 }
91 
92 static void
find_freqs(void)93 find_freqs(void)
94 {
95    char *path;
96 
97    dev.min_freq = ~0;
98    dev.max_freq = 0;
99 
100    (void)asprintf(&path, "%s/%s", dev.dtnode, "qcom,gpu-pwrlevels");
101 
102    nftw(path, find_freqs_fn, 64, 0);
103 
104    free(path);
105 }
106 
107 static const char *compatibles[] = {
108    "qcom,adreno-3xx",
109    "qcom,kgsl-3d0",
110    "amd,imageon",
111    "qcom,adreno",
112 };
113 
114 /**
115  * compatstrs is a list of compatible strings separated by null, ie.
116  *
117  *       compatible = "qcom,adreno-630.2", "qcom,adreno";
118  *
119  * would result in "qcom,adreno-630.2\0qcom,adreno\0"
120  */
121 static bool
match_compatible(char * compatstrs,int sz)122 match_compatible(char *compatstrs, int sz)
123 {
124    while (sz > 0) {
125       char *compatible = compatstrs;
126 
127       for (unsigned i = 0; i < ARRAY_SIZE(compatibles); i++) {
128          if (strcmp(compatible, compatibles[i]) == 0) {
129             return true;
130          }
131       }
132 
133       compatstrs += strlen(compatible) + 1;
134       sz -= strlen(compatible) + 1;
135    }
136    return false;
137 }
138 
139 static int
find_device_fn(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)140 find_device_fn(const char *fpath, const struct stat *sb, int typeflag,
141                struct FTW *ftwbuf)
142 {
143    const char *fname = fpath + ftwbuf->base;
144    size_t sz;
145 
146    if (strcmp(fname, "compatible") == 0) {
147       char *str = os_read_file(fpath, &sz);
148       if (match_compatible(str, sz)) {
149          int dlen = strlen(fpath) - strlen("/compatible");
150          dev.dtnode = malloc(dlen + 1);
151          memcpy(dev.dtnode, fpath, dlen);
152          dev.dtnode[dlen] = '\0';
153          printf("found dt node: %s\n", dev.dtnode);
154 
155          char buf[dlen + sizeof("/../#address-cells") + 1];
156          size_t sz;
157          int *val;
158 
159          sprintf(buf, "%s/../#address-cells", dev.dtnode);
160          val = (int *)os_read_file(buf, &sz);
161          dev.address_cells = ntohl(*val);
162          free(val);
163 
164          sprintf(buf, "%s/../#size-cells", dev.dtnode);
165          val = (int *)os_read_file(buf, &sz);
166          dev.size_cells = ntohl(*val);
167          free(val);
168 
169          printf("#address-cells=%d, #size-cells=%d\n", dev.address_cells,
170                 dev.size_cells);
171       }
172       free(str);
173    }
174    if (dev.dtnode) {
175       /* we found it! */
176       return 1;
177    }
178    return 0;
179 }
180 
181 static bool
find_device(void)182 find_device(void)
183 {
184    int ret;
185    uint32_t *buf, *b;
186 
187    if (dev.dtnode)
188       return true;
189 
190    ret = nftw("/proc/device-tree/", find_device_fn, 64, 0);
191    if (ret < 0)
192       return false;
193 
194    if (!dev.dtnode)
195       return false;
196 
197    b = buf = readdt("reg");
198 
199    if (dev.address_cells == 2) {
200       uint32_t u[2] = {ntohl(buf[0]), ntohl(buf[1])};
201       dev.base = (((uint64_t)u[0]) << 32) | u[1];
202       buf += 2;
203    } else {
204       dev.base = ntohl(buf[0]);
205       buf += 1;
206    }
207 
208    if (dev.size_cells == 2) {
209       uint32_t u[2] = {ntohl(buf[0]), ntohl(buf[1])};
210       dev.size = (((uint64_t)u[0]) << 32) | u[1];
211       buf += 2;
212    } else {
213       dev.size = ntohl(buf[0]);
214       buf += 1;
215    }
216 
217    free(b);
218 
219    printf("i/o region at %08" PRIx64 " (size: %x)\n", dev.base, dev.size);
220 
221    find_freqs();
222 
223    printf("min_freq=%u, max_freq=%u\n", dev.min_freq, dev.max_freq);
224 
225    return true;
226 }
227 
228 bool
fd_dt_find_freqs(uint32_t * min_freq,uint32_t * max_freq)229 fd_dt_find_freqs(uint32_t *min_freq, uint32_t *max_freq)
230 {
231    if (!find_device())
232       return false;
233 
234    *min_freq = dev.min_freq;
235    *max_freq = dev.max_freq;
236 
237    return true;
238 }
239 
240 void *
fd_dt_find_io(void)241 fd_dt_find_io(void)
242 {
243    if (!find_device())
244       return NULL;
245 
246    int fd = open("/dev/mem", O_RDWR | O_SYNC);
247    if (fd < 0)
248       return NULL;
249 
250    void *io =
251       mmap(0, dev.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, dev.base);
252    close(fd);
253    if (io == MAP_FAILED)
254       return NULL;
255 
256    return io;
257 }
258