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