aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBurt P <pburt0@gmail.com>2018-09-28 20:43:41 -0500
committerLeandro A. F. Pereira <leandro@hardinfo.org>2018-10-22 19:45:03 -0700
commit61ffa17d01ef8668def03e15c33d1bd0dc9be93e (patch)
treee0f92bde2f13d5ae246f41d54678ed13ac1198cd
parent7c3ba61e9e433bbad816833320c39cf5bb8afcd8 (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.c127
-rw-r--r--hardinfo/gpu_util.c5
-rw-r--r--includes/dt_util.h15
-rw-r--r--includes/gpu_util.h2
-rw-r--r--modules/devices/gpu.c35
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;
}