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
43static 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
60static void *
61readdt(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
74static int
75find_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
92static void
93find_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
107static 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 */
121static bool
122match_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
139static int
140find_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
181static bool
182find_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
228bool
229fd_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
240void *
241fd_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