17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2021 Google, Inc.
37ec681f3Smrg * All Rights Reserved.
47ec681f3Smrg *
57ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
67ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
77ec681f3Smrg * to deal in the Software without restriction, including without limitation
87ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
97ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
107ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
117ec681f3Smrg *
127ec681f3Smrg * The above copyright notice and this permission notice (including the next
137ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
147ec681f3Smrg * Software.
157ec681f3Smrg *
167ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
197ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
207ec681f3Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
217ec681f3Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
227ec681f3Smrg * OTHER DEALINGS IN THE SOFTWARE.
237ec681f3Smrg */
247ec681f3Smrg
257ec681f3Smrg#include <fcntl.h>
267ec681f3Smrg#include <ftw.h>
277ec681f3Smrg#include <inttypes.h>
287ec681f3Smrg#include <stdint.h>
297ec681f3Smrg#include <stdio.h>
307ec681f3Smrg#include <stdlib.h>
317ec681f3Smrg#include <string.h>
327ec681f3Smrg#include <unistd.h>
337ec681f3Smrg#include <arpa/inet.h>
347ec681f3Smrg#include <sys/mman.h>
357ec681f3Smrg#include <sys/stat.h>
367ec681f3Smrg#include <sys/types.h>
377ec681f3Smrg
387ec681f3Smrg#include "util/macros.h"
397ec681f3Smrg#include "util/os_file.h"
407ec681f3Smrg
417ec681f3Smrg#include "freedreno_dt.h"
427ec681f3Smrg
437ec681f3Smrgstatic struct {
447ec681f3Smrg   char *dtnode;
457ec681f3Smrg   int address_cells, size_cells;
467ec681f3Smrg   uint64_t base;
477ec681f3Smrg   uint32_t size;
487ec681f3Smrg   uint32_t min_freq;
497ec681f3Smrg   uint32_t max_freq;
507ec681f3Smrg} dev;
517ec681f3Smrg
527ec681f3Smrg/*
537ec681f3Smrg * code to find stuff in /proc/device-tree:
547ec681f3Smrg *
557ec681f3Smrg * NOTE: if we sampled the counters from the cmdstream, we could avoid needing
567ec681f3Smrg * /dev/mem and /proc/device-tree crawling.  OTOH when the GPU is heavily loaded
577ec681f3Smrg * we would be competing with whatever else is using the GPU.
587ec681f3Smrg */
597ec681f3Smrg
607ec681f3Smrgstatic void *
617ec681f3Smrgreaddt(const char *node)
627ec681f3Smrg{
637ec681f3Smrg   char *path;
647ec681f3Smrg   void *buf;
657ec681f3Smrg   size_t sz;
667ec681f3Smrg
677ec681f3Smrg   (void)asprintf(&path, "%s/%s", dev.dtnode, node);
687ec681f3Smrg   buf = os_read_file(path, &sz);
697ec681f3Smrg   free(path);
707ec681f3Smrg
717ec681f3Smrg   return buf;
727ec681f3Smrg}
737ec681f3Smrg
747ec681f3Smrgstatic int
757ec681f3Smrgfind_freqs_fn(const char *fpath, const struct stat *sb, int typeflag,
767ec681f3Smrg              struct FTW *ftwbuf)
777ec681f3Smrg{
787ec681f3Smrg   const char *fname = fpath + ftwbuf->base;
797ec681f3Smrg   size_t sz;
807ec681f3Smrg
817ec681f3Smrg   if (strcmp(fname, "qcom,gpu-freq") == 0) {
827ec681f3Smrg      uint32_t *buf = (uint32_t *)os_read_file(fpath, &sz);
837ec681f3Smrg      uint32_t freq = ntohl(buf[0]);
847ec681f3Smrg      free(buf);
857ec681f3Smrg      dev.max_freq = MAX2(dev.max_freq, freq);
867ec681f3Smrg      dev.min_freq = MIN2(dev.min_freq, freq);
877ec681f3Smrg   }
887ec681f3Smrg
897ec681f3Smrg   return 0;
907ec681f3Smrg}
917ec681f3Smrg
927ec681f3Smrgstatic void
937ec681f3Smrgfind_freqs(void)
947ec681f3Smrg{
957ec681f3Smrg   char *path;
967ec681f3Smrg
977ec681f3Smrg   dev.min_freq = ~0;
987ec681f3Smrg   dev.max_freq = 0;
997ec681f3Smrg
1007ec681f3Smrg   (void)asprintf(&path, "%s/%s", dev.dtnode, "qcom,gpu-pwrlevels");
1017ec681f3Smrg
1027ec681f3Smrg   nftw(path, find_freqs_fn, 64, 0);
1037ec681f3Smrg
1047ec681f3Smrg   free(path);
1057ec681f3Smrg}
1067ec681f3Smrg
1077ec681f3Smrgstatic const char *compatibles[] = {
1087ec681f3Smrg   "qcom,adreno-3xx",
1097ec681f3Smrg   "qcom,kgsl-3d0",
1107ec681f3Smrg   "amd,imageon",
1117ec681f3Smrg   "qcom,adreno",
1127ec681f3Smrg};
1137ec681f3Smrg
1147ec681f3Smrg/**
1157ec681f3Smrg * compatstrs is a list of compatible strings separated by null, ie.
1167ec681f3Smrg *
1177ec681f3Smrg *       compatible = "qcom,adreno-630.2", "qcom,adreno";
1187ec681f3Smrg *
1197ec681f3Smrg * would result in "qcom,adreno-630.2\0qcom,adreno\0"
1207ec681f3Smrg */
1217ec681f3Smrgstatic bool
1227ec681f3Smrgmatch_compatible(char *compatstrs, int sz)
1237ec681f3Smrg{
1247ec681f3Smrg   while (sz > 0) {
1257ec681f3Smrg      char *compatible = compatstrs;
1267ec681f3Smrg
1277ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(compatibles); i++) {
1287ec681f3Smrg         if (strcmp(compatible, compatibles[i]) == 0) {
1297ec681f3Smrg            return true;
1307ec681f3Smrg         }
1317ec681f3Smrg      }
1327ec681f3Smrg
1337ec681f3Smrg      compatstrs += strlen(compatible) + 1;
1347ec681f3Smrg      sz -= strlen(compatible) + 1;
1357ec681f3Smrg   }
1367ec681f3Smrg   return false;
1377ec681f3Smrg}
1387ec681f3Smrg
1397ec681f3Smrgstatic int
1407ec681f3Smrgfind_device_fn(const char *fpath, const struct stat *sb, int typeflag,
1417ec681f3Smrg               struct FTW *ftwbuf)
1427ec681f3Smrg{
1437ec681f3Smrg   const char *fname = fpath + ftwbuf->base;
1447ec681f3Smrg   size_t sz;
1457ec681f3Smrg
1467ec681f3Smrg   if (strcmp(fname, "compatible") == 0) {
1477ec681f3Smrg      char *str = os_read_file(fpath, &sz);
1487ec681f3Smrg      if (match_compatible(str, sz)) {
1497ec681f3Smrg         int dlen = strlen(fpath) - strlen("/compatible");
1507ec681f3Smrg         dev.dtnode = malloc(dlen + 1);
1517ec681f3Smrg         memcpy(dev.dtnode, fpath, dlen);
1527ec681f3Smrg         dev.dtnode[dlen] = '\0';
1537ec681f3Smrg         printf("found dt node: %s\n", dev.dtnode);
1547ec681f3Smrg
1557ec681f3Smrg         char buf[dlen + sizeof("/../#address-cells") + 1];
1567ec681f3Smrg         size_t sz;
1577ec681f3Smrg         int *val;
1587ec681f3Smrg
1597ec681f3Smrg         sprintf(buf, "%s/../#address-cells", dev.dtnode);
1607ec681f3Smrg         val = (int *)os_read_file(buf, &sz);
1617ec681f3Smrg         dev.address_cells = ntohl(*val);
1627ec681f3Smrg         free(val);
1637ec681f3Smrg
1647ec681f3Smrg         sprintf(buf, "%s/../#size-cells", dev.dtnode);
1657ec681f3Smrg         val = (int *)os_read_file(buf, &sz);
1667ec681f3Smrg         dev.size_cells = ntohl(*val);
1677ec681f3Smrg         free(val);
1687ec681f3Smrg
1697ec681f3Smrg         printf("#address-cells=%d, #size-cells=%d\n", dev.address_cells,
1707ec681f3Smrg                dev.size_cells);
1717ec681f3Smrg      }
1727ec681f3Smrg      free(str);
1737ec681f3Smrg   }
1747ec681f3Smrg   if (dev.dtnode) {
1757ec681f3Smrg      /* we found it! */
1767ec681f3Smrg      return 1;
1777ec681f3Smrg   }
1787ec681f3Smrg   return 0;
1797ec681f3Smrg}
1807ec681f3Smrg
1817ec681f3Smrgstatic bool
1827ec681f3Smrgfind_device(void)
1837ec681f3Smrg{
1847ec681f3Smrg   int ret;
1857ec681f3Smrg   uint32_t *buf, *b;
1867ec681f3Smrg
1877ec681f3Smrg   if (dev.dtnode)
1887ec681f3Smrg      return true;
1897ec681f3Smrg
1907ec681f3Smrg   ret = nftw("/proc/device-tree/", find_device_fn, 64, 0);
1917ec681f3Smrg   if (ret < 0)
1927ec681f3Smrg      return false;
1937ec681f3Smrg
1947ec681f3Smrg   if (!dev.dtnode)
1957ec681f3Smrg      return false;
1967ec681f3Smrg
1977ec681f3Smrg   b = buf = readdt("reg");
1987ec681f3Smrg
1997ec681f3Smrg   if (dev.address_cells == 2) {
2007ec681f3Smrg      uint32_t u[2] = {ntohl(buf[0]), ntohl(buf[1])};
2017ec681f3Smrg      dev.base = (((uint64_t)u[0]) << 32) | u[1];
2027ec681f3Smrg      buf += 2;
2037ec681f3Smrg   } else {
2047ec681f3Smrg      dev.base = ntohl(buf[0]);
2057ec681f3Smrg      buf += 1;
2067ec681f3Smrg   }
2077ec681f3Smrg
2087ec681f3Smrg   if (dev.size_cells == 2) {
2097ec681f3Smrg      uint32_t u[2] = {ntohl(buf[0]), ntohl(buf[1])};
2107ec681f3Smrg      dev.size = (((uint64_t)u[0]) << 32) | u[1];
2117ec681f3Smrg      buf += 2;
2127ec681f3Smrg   } else {
2137ec681f3Smrg      dev.size = ntohl(buf[0]);
2147ec681f3Smrg      buf += 1;
2157ec681f3Smrg   }
2167ec681f3Smrg
2177ec681f3Smrg   free(b);
2187ec681f3Smrg
2197ec681f3Smrg   printf("i/o region at %08" PRIx64 " (size: %x)\n", dev.base, dev.size);
2207ec681f3Smrg
2217ec681f3Smrg   find_freqs();
2227ec681f3Smrg
2237ec681f3Smrg   printf("min_freq=%u, max_freq=%u\n", dev.min_freq, dev.max_freq);
2247ec681f3Smrg
2257ec681f3Smrg   return true;
2267ec681f3Smrg}
2277ec681f3Smrg
2287ec681f3Smrgbool
2297ec681f3Smrgfd_dt_find_freqs(uint32_t *min_freq, uint32_t *max_freq)
2307ec681f3Smrg{
2317ec681f3Smrg   if (!find_device())
2327ec681f3Smrg      return false;
2337ec681f3Smrg
2347ec681f3Smrg   *min_freq = dev.min_freq;
2357ec681f3Smrg   *max_freq = dev.max_freq;
2367ec681f3Smrg
2377ec681f3Smrg   return true;
2387ec681f3Smrg}
2397ec681f3Smrg
2407ec681f3Smrgvoid *
2417ec681f3Smrgfd_dt_find_io(void)
2427ec681f3Smrg{
2437ec681f3Smrg   if (!find_device())
2447ec681f3Smrg      return NULL;
2457ec681f3Smrg
2467ec681f3Smrg   int fd = open("/dev/mem", O_RDWR | O_SYNC);
2477ec681f3Smrg   if (fd < 0)
2487ec681f3Smrg      return NULL;
2497ec681f3Smrg
2507ec681f3Smrg   void *io =
2517ec681f3Smrg      mmap(0, dev.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, dev.base);
2527ec681f3Smrg   close(fd);
2537ec681f3Smrg   if (io == MAP_FAILED)
2547ec681f3Smrg      return NULL;
2557ec681f3Smrg
2567ec681f3Smrg   return io;
2577ec681f3Smrg}
258