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