diff --git a/src/freedreno/perfcntrs/fdperf.c b/src/freedreno/perfcntrs/fdperf.c index 8aec968e93c..a84786695ca 100644 --- a/src/freedreno/perfcntrs/fdperf.c +++ b/src/freedreno/perfcntrs/fdperf.c @@ -22,22 +22,14 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include #include -#include #include -#include -#include #include #include #include #include #include -#include -#include -#include #include -#include #include #include #include @@ -48,6 +40,7 @@ #include "util/os_file.h" +#include "freedreno_dt.h" #include "freedreno_perfcntr.h" #define MAX_CNTR_PER_GROUP 24 @@ -82,10 +75,6 @@ struct counter_group { }; static struct { - char *dtnode; - int address_cells, size_cells; - uint64_t base; - uint32_t size; void *io; uint32_t chipid; uint32_t min_freq; @@ -126,146 +115,10 @@ delta(uint32_t a, uint32_t b) return b - a; } -/* - * code to find stuff in /proc/device-tree: - * - * NOTE: if we sampled the counters from the cmdstream, we could avoid needing - * /dev/mem and /proc/device-tree crawling. OTOH when the GPU is heavily loaded - * we would be competing with whatever else is using the GPU. - */ - -static void * -readdt(const char *node) -{ - char *path; - void *buf; - size_t sz; - - (void) asprintf(&path, "%s/%s", dev.dtnode, node); - buf = os_read_file(path, &sz); - free(path); - - return buf; -} - -static int -find_freqs_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -{ - const char *fname = fpath + ftwbuf->base; - size_t sz; - - if (strcmp(fname, "qcom,gpu-freq") == 0) { - uint32_t *buf = (uint32_t *)os_read_file(fpath, &sz); - uint32_t freq = ntohl(buf[0]); - free(buf); - dev.max_freq = MAX2(dev.max_freq, freq); - dev.min_freq = MIN2(dev.min_freq, freq); - } - - return 0; -} - -static void -find_freqs(void) -{ - char *path; - int ret; - - dev.min_freq = ~0; - dev.max_freq = 0; - - (void) asprintf(&path, "%s/%s", dev.dtnode, "qcom,gpu-pwrlevels"); - - ret = nftw(path, find_freqs_fn, 64, 0); - if (ret < 0) - err(1, "could not find power levels"); - - free(path); -} - -static const char * compatibles[] = { - "qcom,adreno-3xx", - "qcom,kgsl-3d0", - "amd,imageon", - "qcom,adreno", -}; - -/** - * compatstrs is a list of compatible strings separated by null, ie. - * - * compatible = "qcom,adreno-630.2", "qcom,adreno"; - * - * would result in "qcom,adreno-630.2\0qcom,adreno\0" - */ -static bool match_compatible(char *compatstrs, int sz) -{ - while (sz > 0) { - char *compatible = compatstrs; - - for (unsigned i = 0; i < ARRAY_SIZE(compatibles); i++) { - if (strcmp(compatible, compatibles[i]) == 0) { - return true; - } - } - - compatstrs += strlen(compatible) + 1; - sz -= strlen(compatible) + 1; - } - return false; -} - -static int -find_device_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -{ - const char *fname = fpath + ftwbuf->base; - size_t sz; - - if (strcmp(fname, "compatible") == 0) { - char *str = os_read_file(fpath, &sz); - if (match_compatible(str, sz)) { - int dlen = strlen(fpath) - strlen("/compatible"); - dev.dtnode = malloc(dlen + 1); - memcpy(dev.dtnode, fpath, dlen); - printf("found dt node: %s\n", dev.dtnode); - - char buf[dlen + sizeof("/../#address-cells") + 1]; - size_t sz; - int *val; - - sprintf(buf, "%s/../#address-cells", dev.dtnode); - val = (int *)os_read_file(buf, &sz); - dev.address_cells = ntohl(*val); - free(val); - - sprintf(buf, "%s/../#size-cells", dev.dtnode); - val = (int *)os_read_file(buf, &sz); - dev.size_cells = ntohl(*val); - free(val); - - printf("#address-cells=%d, #size-cells=%d\n", - dev.address_cells, dev.size_cells); - } - free(str); - } - if (dev.dtnode) { - /* we found it! */ - return 1; - } - return 0; -} - static void find_device(void) { int ret, fd; - uint32_t *buf, *b; - - ret = nftw("/proc/device-tree/", find_device_fn, 64, 0); - if (ret < 0) - err(1, "could not find adreno gpu"); - - if (!dev.dtnode) - errx(1, "could not find qcom,adreno-3xx node"); fd = drmOpenWithType("msm", NULL, DRM_NODE_RENDER); if (fd < 0) @@ -289,37 +142,14 @@ find_device(void) ((chipid) >> 0) & 0xff printf("device: a%"CHIP_FMT"\n", CHIP_ARGS(dev.chipid)); - b = buf = readdt("reg"); - - if (dev.address_cells == 2) { - uint32_t u[2] = { ntohl(buf[0]), ntohl(buf[1]) }; - dev.base = (((uint64_t)u[0]) << 32) | u[1]; - buf += 2; - } else { - dev.base = ntohl(buf[0]); - buf += 1; - } - - if (dev.size_cells == 2) { - uint32_t u[2] = { ntohl(buf[0]), ntohl(buf[1]) }; - dev.size = (((uint64_t)u[0]) << 32) | u[1]; - buf += 2; - } else { - dev.size = ntohl(buf[0]); - buf += 1; - } - - free(b); - - printf("i/o region at %08"PRIx64" (size: %x)\n", dev.base, dev.size); - /* try MAX_FREQ first as that will work regardless of old dt * dt bindings vs upstream bindings: */ ret = fd_pipe_get_param(dev.pipe, FD_MAX_FREQ, &val); if (ret) { printf("falling back to parsing DT bindings for freq\n"); - find_freqs(); + if (!fd_dt_find_freqs(&dev.min_freq, &dev.max_freq)) + err(1, "could not find GPU freqs"); } else { dev.min_freq = 0; dev.max_freq = val; @@ -327,13 +157,8 @@ find_device(void) printf("min_freq=%u, max_freq=%u\n", dev.min_freq, dev.max_freq); - fd = open("/dev/mem", O_RDWR | O_SYNC); - if (fd < 0) - err(1, "could not open /dev/mem"); - - dev.io = mmap(0, dev.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, dev.base); - if (dev.io == MAP_FAILED) { - close(fd); + dev.io = fd_dt_find_io(); + if (!dev.io) { err(1, "could not map device"); } } diff --git a/src/freedreno/perfcntrs/freedreno_dt.c b/src/freedreno/perfcntrs/freedreno_dt.c new file mode 100644 index 00000000000..4dba61854bb --- /dev/null +++ b/src/freedreno/perfcntrs/freedreno_dt.c @@ -0,0 +1,255 @@ +/* + * Copyright © 2021 Google, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/macros.h" +#include "util/os_file.h" + +#include "freedreno_dt.h" + +static struct { + char *dtnode; + int address_cells, size_cells; + uint64_t base; + uint32_t size; + uint32_t min_freq; + uint32_t max_freq; +} dev; + + +/* + * code to find stuff in /proc/device-tree: + * + * NOTE: if we sampled the counters from the cmdstream, we could avoid needing + * /dev/mem and /proc/device-tree crawling. OTOH when the GPU is heavily loaded + * we would be competing with whatever else is using the GPU. + */ + +static void * +readdt(const char *node) +{ + char *path; + void *buf; + size_t sz; + + (void) asprintf(&path, "%s/%s", dev.dtnode, node); + buf = os_read_file(path, &sz); + free(path); + + return buf; +} + +static int +find_freqs_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + const char *fname = fpath + ftwbuf->base; + size_t sz; + + if (strcmp(fname, "qcom,gpu-freq") == 0) { + uint32_t *buf = (uint32_t *)os_read_file(fpath, &sz); + uint32_t freq = ntohl(buf[0]); + free(buf); + dev.max_freq = MAX2(dev.max_freq, freq); + dev.min_freq = MIN2(dev.min_freq, freq); + } + + return 0; +} + +static void +find_freqs(void) +{ + char *path; + + dev.min_freq = ~0; + dev.max_freq = 0; + + (void) asprintf(&path, "%s/%s", dev.dtnode, "qcom,gpu-pwrlevels"); + + nftw(path, find_freqs_fn, 64, 0); + + free(path); +} + +static const char * compatibles[] = { + "qcom,adreno-3xx", + "qcom,kgsl-3d0", + "amd,imageon", + "qcom,adreno", +}; + +/** + * compatstrs is a list of compatible strings separated by null, ie. + * + * compatible = "qcom,adreno-630.2", "qcom,adreno"; + * + * would result in "qcom,adreno-630.2\0qcom,adreno\0" + */ +static bool match_compatible(char *compatstrs, int sz) +{ + while (sz > 0) { + char *compatible = compatstrs; + + for (unsigned i = 0; i < ARRAY_SIZE(compatibles); i++) { + if (strcmp(compatible, compatibles[i]) == 0) { + return true; + } + } + + compatstrs += strlen(compatible) + 1; + sz -= strlen(compatible) + 1; + } + return false; +} + +static int +find_device_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + const char *fname = fpath + ftwbuf->base; + size_t sz; + + if (strcmp(fname, "compatible") == 0) { + char *str = os_read_file(fpath, &sz); + if (match_compatible(str, sz)) { + int dlen = strlen(fpath) - strlen("/compatible"); + dev.dtnode = malloc(dlen + 1); + memcpy(dev.dtnode, fpath, dlen); + dev.dtnode[dlen] = '\0'; + printf("found dt node: %s\n", dev.dtnode); + + char buf[dlen + sizeof("/../#address-cells") + 1]; + size_t sz; + int *val; + + sprintf(buf, "%s/../#address-cells", dev.dtnode); + val = (int *)os_read_file(buf, &sz); + dev.address_cells = ntohl(*val); + free(val); + + sprintf(buf, "%s/../#size-cells", dev.dtnode); + val = (int *)os_read_file(buf, &sz); + dev.size_cells = ntohl(*val); + free(val); + + printf("#address-cells=%d, #size-cells=%d\n", + dev.address_cells, dev.size_cells); + } + free(str); + } + if (dev.dtnode) { + /* we found it! */ + return 1; + } + return 0; +} + +static bool +find_device(void) +{ + int ret; + uint32_t *buf, *b; + + if (dev.dtnode) + return true; + + ret = nftw("/proc/device-tree/", find_device_fn, 64, 0); + if (ret < 0) + return false; + + if (!dev.dtnode) + return false; + + b = buf = readdt("reg"); + + if (dev.address_cells == 2) { + uint32_t u[2] = { ntohl(buf[0]), ntohl(buf[1]) }; + dev.base = (((uint64_t)u[0]) << 32) | u[1]; + buf += 2; + } else { + dev.base = ntohl(buf[0]); + buf += 1; + } + + if (dev.size_cells == 2) { + uint32_t u[2] = { ntohl(buf[0]), ntohl(buf[1]) }; + dev.size = (((uint64_t)u[0]) << 32) | u[1]; + buf += 2; + } else { + dev.size = ntohl(buf[0]); + buf += 1; + } + + free(b); + + printf("i/o region at %08"PRIx64" (size: %x)\n", dev.base, dev.size); + + find_freqs(); + + printf("min_freq=%u, max_freq=%u\n", dev.min_freq, dev.max_freq); + + return true; +} + +bool +fd_dt_find_freqs(uint32_t *min_freq, uint32_t *max_freq) +{ + if (!find_device()) + return false; + + *min_freq = dev.min_freq; + *max_freq = dev.max_freq; + + return true; +} + +void * +fd_dt_find_io(void) +{ + if (!find_device()) + return NULL; + + int fd = open("/dev/mem", O_RDWR | O_SYNC); + if (fd < 0) + return NULL; + + void *io = mmap(0, dev.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, dev.base); + if (io == MAP_FAILED) { + close(fd); + return NULL; + } + + return io; +} diff --git a/src/freedreno/perfcntrs/freedreno_dt.h b/src/freedreno/perfcntrs/freedreno_dt.h new file mode 100644 index 00000000000..c08916e68c2 --- /dev/null +++ b/src/freedreno/perfcntrs/freedreno_dt.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2021 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef FREEDRENO_DT_H_ +#define FREEDRENO_DT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A helper for extracting information about the GPU from devicetree, and + * mapping it's i/o space, etc. + * + * Note, not-reentrant (due to use of nftw(), etc). + */ + +bool fd_dt_find_freqs(uint32_t *min_freq, uint32_t *max_freq); +void * fd_dt_find_io(void); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* FREEDRENO_DT_H_ */ diff --git a/src/freedreno/perfcntrs/meson.build b/src/freedreno/perfcntrs/meson.build index 48370a32b7d..be55df14171 100644 --- a/src/freedreno/perfcntrs/meson.build +++ b/src/freedreno/perfcntrs/meson.build @@ -22,6 +22,8 @@ libfreedreno_perfcntrs_files = files( 'fd2_perfcntr.c', 'fd5_perfcntr.c', 'fd6_perfcntr.c', + 'freedreno_dt.c', + 'freedreno_dt.h', 'freedreno_perfcntr.c', 'freedreno_perfcntr.h', )