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