diff options
author | Burt P <pburt0@gmail.com> | 2018-09-28 20:43:41 -0500 |
---|---|---|
committer | Leandro A. F. Pereira <leandro@hardinfo.org> | 2018-10-22 19:45:03 -0700 |
commit | 61ffa17d01ef8668def03e15c33d1bd0dc9be93e (patch) | |
tree | e0f92bde2f13d5ae246f41d54678ed13ac1198cd | |
parent | 7c3ba61e9e433bbad816833320c39cf5bb8afcd8 (diff) |
devicetree: get opp-v2 freq range for gpu, if available
* opp-v2 = operating-points-v2, frequency scaling information
from device tree that can be used for cpu, gpu, etc.
* adds helper function to get the opp-v2 range of frequencies
for a node, dtr_get_opp_range() in dt_util.c
* adds a freq range in opp-v2 property for a node in dt
* reports a gpu's max clock frequency if avaiable via opp-v2
Signed-off-by: Burt P <pburt0@gmail.com>
-rw-r--r-- | hardinfo/dt_util.c | 127 | ||||
-rw-r--r-- | hardinfo/gpu_util.c | 5 | ||||
-rw-r--r-- | includes/dt_util.h | 15 | ||||
-rw-r--r-- | includes/gpu_util.h | 2 | ||||
-rw-r--r-- | modules/devices/gpu.c | 35 |
5 files changed, 176 insertions, 8 deletions
diff --git a/hardinfo/dt_util.c b/hardinfo/dt_util.c index b7b6073f..49d08f8b 100644 --- a/hardinfo/dt_util.c +++ b/hardinfo/dt_util.c @@ -52,6 +52,7 @@ static struct { /* operating-points-v2: */ /* https://www.kernel.org/doc/Documentation/devicetree/bindings/opp/opp.txt */ + { "operating-points-v2", DTP_PH_REF_OPP2 }, { "opp-hz", DTP_UINT64 }, { "opp-microvolt", DTP_UINT }, { "opp-microvolt-L0", DTP_UINT }, /* opp-microvolt-<named>, but this kind of */ @@ -482,6 +483,21 @@ uint32_t dtr_get_prop_u32(dtr *s, dtr_obj *node, const char *name) { return ret; } +uint64_t dtr_get_prop_u64(dtr *s, dtr_obj *node, const char *name) { + dtr_obj *prop; + char *ptmp; + uint64_t ret = 0; + + ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); + prop = dtr_obj_read(s, ptmp); + if (prop != NULL && prop->data != NULL) { + ret = be64toh(*prop->data_int64); + dtr_obj_free(prop); + } + g_free(ptmp); + return ret; +} + int dtr_guess_type(dtr_obj *obj) { char *tmp, *dash; int i = 0, anc = 0, might_be_str = 1; @@ -537,7 +553,7 @@ int dtr_guess_type(dtr_obj *obj) { return DTP_UNK; } -char *dtr_elem_phref(dtr *s, dt_uint e, int show_path) { +char *dtr_elem_phref(dtr *s, dt_uint e, int show_path, const char *extra) { const char *ph_path, *al_label; char *ret = NULL; ph_path = dtr_phandle_lookup(s, be32toh(e)); @@ -546,12 +562,12 @@ char *dtr_elem_phref(dtr *s, dt_uint e, int show_path) { al_label = dtr_symbol_lookup_by_path(s, ph_path); if (al_label != NULL) { if (show_path) - ret = g_strdup_printf("&%s (%s)", al_label, ph_path); + ret = g_strdup_printf("&%s (%s) %s", al_label, ph_path, extra ? extra : ""); else - ret = g_strdup_printf("&%s", al_label); + ret = g_strdup_printf("&%s %s", al_label, extra ? extra : ""); } else { if (show_path) - ret = g_strdup_printf("0x%x (%s)", be32toh(e), ph_path); + ret = g_strdup_printf("0x%x (%s) %s", be32toh(e), ph_path, extra ? extra : ""); } } if (ret == NULL) @@ -559,6 +575,20 @@ char *dtr_elem_phref(dtr *s, dt_uint e, int show_path) { return ret; } +char *dtr_elem_oppv2(dtr_obj* obj) { + char opp_str[512] = ""; + dtr_obj *parent = dtr_get_parent_obj(obj); + if (parent) { + dt_opp_range *opp = dtr_get_opp_range(obj->dt, parent->path); + if (opp) { + snprintf(opp_str, 511, "[%d - %d %s]", opp->khz_min, opp->khz_max, _("kHz")); + free(opp); + } + dtr_obj_free(parent); + } + return dtr_elem_phref(obj->dt, *obj->data_int, 1, opp_str); +} + char *dtr_elem_hex(dt_uint e) { return g_strdup_printf("0x%x", be32toh(e) ); } @@ -643,7 +673,7 @@ char *dtr_list_override(dtr_obj *obj) { int l, consumed = 0; src = obj->data_str; while (consumed + 5 <= obj->length) { - ph = dtr_elem_phref(obj->dt, *(dt_uint*)src, 1); + ph = dtr_elem_phref(obj->dt, *(dt_uint*)src, 1, NULL); src += 4; consumed += 4; l = strlen(src) + 1; /* consume the null */ str = dtr_list_str0(src, l); @@ -682,7 +712,7 @@ char *dtr_list_phref(dtr_obj *obj, char *ext_cell_prop) { ext_cells = 0; else ext_cells = dtr_get_phref_prop(obj->dt, be32toh(obj->data_int[i]), ext_cell_prop); - ph = dtr_elem_phref(obj->dt, obj->data_int[i], 0); i++; + ph = dtr_elem_phref(obj->dt, obj->data_int[i], 0, NULL); i++; if (ext_cells > count - i) ext_cells = count - i; ext = dtr_list_hex((obj->data_int + i), ext_cells); i+=ext_cells; ret = appf(ret, "<%s%s%s>", @@ -814,7 +844,10 @@ char* dtr_str(dtr_obj *obj) { ret = dtr_list_hex(obj->data, obj->length / 4); break; case DTP_PH_REF: - ret = dtr_elem_phref(obj->dt, *obj->data_int, 1); + ret = dtr_elem_phref(obj->dt, *obj->data_int, 1, NULL); + break; + case DTP_PH_REF_OPP2: + ret = dtr_elem_oppv2(obj); break; case DTP_UINT: ret = dtr_elem_uint(*obj->data_int); @@ -895,6 +928,86 @@ int dtr_inh_find(dtr_obj *obj, char *qprop, int limit) { return ret; } +dt_opp_range *dtr_get_opp_range(dtr *s, const char *name) { + dt_opp_range *ret = NULL; + dtr_obj *obj = NULL, *table_obj = NULL, *row_obj = NULL; + uint32_t opp_ph = 0; + const char *opp_table_path = NULL; + char *tab_compat = NULL, *tab_status = NULL; + const gchar *fn; + gchar *full_path; + GDir *dir; + uint64_t khz = 0; + uint32_t lns = 0; + char *row_status = NULL; + + if (!s) + return NULL; + + obj = dtr_obj_read(s, name); + if (!obj) + goto get_opp_finish; + + opp_ph = dtr_get_prop_u32(s, obj, "operating-points-v2"); + if (!opp_ph) + goto get_opp_finish; + + opp_table_path = dtr_phandle_lookup(s, opp_ph); + if (!opp_table_path) + goto get_opp_finish; + + table_obj = dtr_obj_read(s, opp_table_path); + if (!table_obj) + goto get_opp_finish; + + tab_compat = dtr_get_prop_str(s, table_obj, "compatible"); + tab_status = dtr_get_prop_str(s, table_obj, "status"); + + if (!tab_compat || strcmp(tab_compat, "operating-points-v2") != 0) + goto get_opp_finish; + if (tab_status && strcmp(tab_status, "disabled") == 0) + goto get_opp_finish; + + ret = malloc(sizeof(dt_opp_range)); + ret->phandle = opp_ph; + ret->khz_min = ret->khz_max = ret->clock_latency_ns = 0; + + full_path = dtr_obj_full_path(table_obj); + dir = g_dir_open(full_path, 0 , NULL); + if (dir) { + while((fn = g_dir_read_name(dir)) != NULL) { + row_obj = dtr_get_prop_obj(s, table_obj, fn); + if (row_obj->type == DT_NODE) { + row_status = dtr_get_prop_str(s, row_obj, "status"); + if (!row_status || strcmp(row_status, "disabled") != 0) { + khz = dtr_get_prop_u64(s, row_obj, "opp-hz"); + khz /= 1000; /* 64b hz -> 32b khz */ + lns = dtr_get_prop_u32(s, row_obj, "clock-latency-ns"); + if (khz > ret->khz_max) + ret->khz_max = khz; + if (khz < ret->khz_min || ret->khz_min == 0) + ret->khz_min = khz; + ret->clock_latency_ns = lns; + } + } + free(row_status); row_status = NULL; + dtr_obj_free(row_obj); + row_obj = NULL; + } + g_dir_close(dir); + } + g_free(full_path); + +get_opp_finish: + dtr_obj_free(obj); + dtr_obj_free(table_obj); + free(tab_status); + free(tab_compat); + free(row_status); + return ret; +} + + void _dtr_read_aliases(dtr *s) { gchar *dir_path; GDir *dir; diff --git a/hardinfo/gpu_util.c b/hardinfo/gpu_util.c index 4347aad6..dee3c431 100644 --- a/hardinfo/gpu_util.c +++ b/hardinfo/gpu_util.c @@ -97,6 +97,7 @@ void gpud_free(gpud *s) { free(s->drm_dev); free(s->sysfs_drm_path); free(s->dt_compat); + free(s->dt_opp); pcid_free(s->pci_dev); nvgpu_free(s->nv_info); g_free(s); @@ -301,6 +302,10 @@ gpud *dt_soc_gpu() { gpu->dt_status = dtr_get_string(tmp_path, 1); snprintf(tmp_path, 255, "%s/name", dt_gpu_path); gpu->dt_name = dtr_get_string(tmp_path, 1); + gpu->dt_opp = dtr_get_opp_range(dt, dt_gpu_path); + if (gpu->dt_opp) { + gpu->khz_max = gpu->dt_opp->khz_max; + } EMPIFNULL(gpu->dt_name); EMPIFNULL(gpu->dt_status); diff --git a/includes/dt_util.h b/includes/dt_util.h index 0fbd53cf..62b215fc 100644 --- a/includes/dt_util.h +++ b/includes/dt_util.h @@ -30,6 +30,7 @@ enum { DTP_CLOCKS, /* <phref, #clock-cells> */ DTP_GPIOS, /* <phref, #gpio-cells> */ DTP_DMAS, /* dma-specifier list */ + DTP_PH_REF_OPP2, /* phandle reference to opp-v2 table */ }; /* simplest, no aliases, doesn't require an existing dt. @@ -55,18 +56,20 @@ char *dtr_obj_alias(dtr_obj *); char *dtr_obj_symbol(dtr_obj *); char *dtr_obj_path(dtr_obj *); /* device tree path */ char *dtr_obj_full_path(dtr_obj *); /* system path */ +dtr_obj *dtr_get_parent_obj(dtr_obj *); /* find property/node 'name' relative to node * if node is NULL, then from root */ dtr_obj *dtr_get_prop_obj(dtr *, dtr_obj *node, const char *name); char *dtr_get_prop_str(dtr *, dtr_obj *node, const char *name); uint32_t dtr_get_prop_u32(dtr *, dtr_obj *node, const char *name); +uint64_t dtr_get_prop_u64(dtr *, dtr_obj *node, const char *name); /* attempts to render the object as a string */ char* dtr_str(dtr_obj *obj); int dtr_guess_type(dtr_obj *obj); -char *dtr_elem_phref(dtr *, dt_uint e, int show_path); +char *dtr_elem_phref(dtr *, dt_uint e, int show_path, const char *extra); char *dtr_elem_hex(dt_uint e); char *dtr_elem_byte(uint8_t e); char *dtr_elem_uint(dt_uint e); @@ -88,4 +91,14 @@ void dtr_msg(dtr *s, char *fmt, ...); * ex: ret = appf(ret, "%s=%s\n", name, value); */ char *appf(char *src, char *fmt, ...); +/* operating-points-v2 */ +typedef struct { + uint32_t phandle; + uint32_t khz_min; + uint32_t khz_max; + uint32_t clock_latency_ns; +} dt_opp_range; + +dt_opp_range *dtr_get_opp_range(dtr *, const char *name); + #endif diff --git a/includes/gpu_util.h b/includes/gpu_util.h index 0523cc81..1eb3c6c4 100644 --- a/includes/gpu_util.h +++ b/includes/gpu_util.h @@ -35,6 +35,7 @@ typedef struct gpud { char *vendor_str; char *device_str; char *location; + uint32_t khz_max; char *drm_dev; char *sysfs_drm_path; @@ -42,6 +43,7 @@ typedef struct gpud { char *dt_compat, *dt_status, *dt_name, *dt_path; const char *dt_vendor, *dt_device; + dt_opp_range *dt_opp; nvgpu *nv_info; /* ... */ diff --git a/modules/devices/gpu.c b/modules/devices/gpu.c index 429b0b0f..75776d7e 100644 --- a/modules/devices/gpu.c +++ b/modules/devices/gpu.c @@ -127,12 +127,19 @@ static void _gpu_pci_dev(gpud* gpu) { } else nv_str = strdup(""); + gchar *freq = g_strdup(_("(Unknown)")); + if (gpu->khz_max > 0) { + freq = g_strdup_printf("%0.2f %s", (double) gpu->khz_max / 1000, _("MHz")); + } + str = g_strdup_printf("[%s]\n" /* Location */ "%s=%s\n" /* DRM Dev */ "%s=%s\n" /* Class */ "%s=[%04x] %s\n" "%s" /* Revision */ "%s=%02x\n" + "[%s]\n" + /* Frequency */ "%s=%s\n" /* NV */ "%s" /* PCIe */ "%s" "[%s]\n" @@ -144,6 +151,8 @@ static void _gpu_pci_dev(gpud* gpu) { _("Class"), p->class, p->class_str, vendor_device_str, _("Revision"), p->revision, + _("Clocks"), + _("Core"), freq, nv_str, pcie_str, _("Driver"), @@ -169,10 +178,28 @@ int _dt_soc_gpu(gpud *gpu) { gchar *device = gpu->device_str; if (vendor == NULL) vendor = UNKSOC; if (device == NULL) device = UNKSOC; + gchar *freq = g_strdup(_("(Unknown)")); + if (gpu->khz_max > 0) { + freq = g_strdup_printf("%0.2f %s", (double) gpu->khz_max / 1000, _("MHz")); + } gchar *key = g_strdup(gpu->id); gchar *name = (vendor == UNKSOC && device == UNKSOC) ? g_strdup(_("Unknown integrated GPU")) : g_strdup_printf("%s %s", vendor, device); + + gchar *opp_str; + if (gpu->dt_opp) { + opp_str = g_strdup_printf("[%s]\n" + /* MinFreq */ "%s=%d %s\n" + /* MaxFreq */ "%s=%d %s\n" + /* Latency */ "%s=%d %s\n", + _("Frequency Scaling"), + _("Minimum"), gpu->dt_opp->khz_min, _("kHz"), + _("Maximum"), gpu->dt_opp->khz_max, _("kHz"), + _("Transition Latency"), gpu->dt_opp->clock_latency_ns, _("ns") ); + } else + opp_str = strdup(""); + gpu_summary_add((gpu->nice_name) ? gpu->nice_name : name); gpu_list = h_strdup_cprintf("$%s$%s=%s\n", gpu_list, key, key, name); gchar *str = g_strdup_printf("[%s]\n" @@ -180,6 +207,9 @@ int _dt_soc_gpu(gpud *gpu) { /* Vendor */ "%s=%s\n" /* Device */ "%s=%s\n" "[%s]\n" + /* Freq */ "%s=%s\n" + /* opp-v2 */ "%s" + "[%s]\n" /* Path */ "%s=%s\n" /* Compat */ "%s=%s\n" /* Status */ "%s=%s\n" @@ -188,6 +218,9 @@ int _dt_soc_gpu(gpud *gpu) { _("Location"), gpu->location, _("Vendor"), vendor, _("Device"), device, + _("Clocks"), + _("Core"), freq, + opp_str, _("Device Tree Node"), _("Path"), gpu->dt_path, _("Compatible"), gpu->dt_compat, @@ -195,6 +228,8 @@ int _dt_soc_gpu(gpud *gpu) { _("Name"), gpu->dt_name ); moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */ + g_free(freq); + g_free(opp_str); return 1; } |