Initial distribution of NVIDIA's power query tool.
Signed-off-by: Anton Staaf <robotboy@chromium.org>
BUG=chrome-os-partner:2381
TEST=run make successfully
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..cfe68e3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+CFLAGS=-std=c99 -Wall -O2
+
+tegra-power-query: power_tools.c seaboard.h power_tools.h
+ $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+ rm tegra-power-query
+
+.PHONY: clean
diff --git a/README b/README
new file mode 100644
index 0000000..4525744
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+Build with 'make'.
+Run with './tegra-power-query'.
+For help, './tegra-power-query -h'.
+
+If you're having trouble, make sure that you have CONFIG_USB_HIDDEV enabled in
+your kernel and that you have read access to the device nodes at
+"/dev/usb/hiddev*".
diff --git a/plot/plot_all.sh b/plot/plot_all.sh
new file mode 100755
index 0000000..5bde934
--- /dev/null
+++ b/plot/plot_all.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+#
+# Example of how to draw a couple of windows displaying all of the captured
+# data in real-time.
+#
+# This uses the "driveGnuPlotStreams.pl" available from
+# http://users.softlab.ntua.gr/~ttsiod/gnuplotStreaming.html
+#
+# At least on my computer, this isn't particularly fast; limiting it to a
+# subset of the data is much faster.
+#
+
+
+../tegra-power-query -s | ./driveGnuPlotStreams.pl \
+ 16 \
+ 2 \
+ 500 500 \
+ 0 0.3 \
+ 0 16 \
+ 500x500+0+30 \
+ 500x500+510+30 \
+ "BAT Voltage" \
+ "BAT Current" \
+ "SYS_SM1 Voltage" \
+ "SYS_SM1 Current" \
+ "SYS_SM2 Voltage" \
+ "SYS_SM2 Current" \
+ "CHRG Voltage" \
+ "CHRG Current" \
+ "DDR2 Voltage" \
+ "DDR2 Current" \
+ "SYS_SM0 Voltage" \
+ "SYS_SM0 Current" \
+ "BL Voltage" \
+ "BL Current" \
+ "PNL Voltage" \
+ "PNL Current" \
+ 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
diff --git a/power_tools.c b/power_tools.c
new file mode 100644
index 0000000..36828be
--- /dev/null
+++ b/power_tools.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2010, NVIDIA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of NVIDIA nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* needed for strdup() and asprintf() */
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <linux/hiddev.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "seaboard.h"
+
+static char *prog_name;
+
+struct state {
+ char *hiddev_filename;
+ int fd;
+ int timestamps;
+ int no_names;
+};
+
+#define outerr(fmt, args...) fprintf(stderr, "%s: " fmt "\n", prog_name, ## args)
+
+#define perr(fmt, args...) outerr(fmt ": %s", ## args, strerror(errno))
+
+/* Constants to identify the HW */
+#define NVIDIA_VENDOR_ID 0x955
+#define USB_DAC_PRODUCT_ID 0x9
+
+static int is_usage_current(int usage) {
+ /* Odd usages are current */
+ return !!(usage & 1);
+}
+
+static int probe_from_usage(int usage) {
+ /* Each probe covers two consecutive usages, so this gets the probe index */
+ return usage >> 1;
+}
+
+static double scale_reading(int input, int usage) {
+ if (is_usage_current(usage)) {
+ static const double scale_factor = 3.3 / 1023. / 100.;
+ double output = input * scale_factor;
+ output /= seaboard_probes[probe_from_usage(usage)].resistance;
+ return output;
+ } else {
+ static const double scale_factor = 3.3 / 1023. / 0.1708;
+ double output = input * scale_factor;
+ return output;
+ }
+}
+
+static void print_data(struct state *s, double value, int usage,
+ uint64_t time) {
+ const char *name = seaboard_probes[probe_from_usage(usage)].name;
+ const char *type = is_usage_current(usage) ? "CURR" : "VOLT";
+
+ if (s->timestamps) {
+ printf("%" PRIu64 ": ", time);
+ }
+
+ if (s->no_names) {
+ printf("%02u:%.16f\n", usage, value);
+ } else {
+ printf("%02u %8s %s %.16f\n", usage, name, type, value);
+ }
+}
+
+static uint64_t gettime64() {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL)) {
+ perr("gettimeofday failed");
+ return 0;
+ }
+
+ return (uint64_t) tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+#define USB_HID_ORDINAL_USAGE_PAGE 0xa
+
+static int read_data(struct state *s) {
+ struct hiddev_event events[1024];
+ ssize_t num_read;
+
+retry:
+ while ((num_read = read(s->fd, &events, sizeof(events))) > 0) {
+ int i;
+ uint64_t time = gettime64();
+
+ for (i = 0; i < num_read / sizeof(events[0]); i += 1) {
+ double scaled_value;
+ int usage_page = events[i].hid >> 16;
+ int usage_code = (events[i].hid & 0xffff) - 1;
+
+ /* All of the "data" reports are "ordinal", skip the rest */
+ if (usage_page != USB_HID_ORDINAL_USAGE_PAGE)
+ continue;
+
+ scaled_value = scale_reading(events[i].value, usage_code);
+
+ print_data(s, scaled_value, usage_code, time);
+ }
+
+ /* Without this, realtime graph may lag */
+ fflush(NULL);
+ }
+
+ if (num_read < 0) {
+ int err = errno;
+ if (err == EINTR) {
+ goto retry;
+ }
+ perr("Failed to read from USB device");
+ return err;
+ }
+
+ return 0;
+}
+
+static int try_open_device(struct state *s, const char *filename) {
+ int fd = -1;
+ struct hiddev_devinfo dev_info = { 0 };
+ int ret;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ ret = errno;
+ perr("failed to open file '%s'", filename);
+ goto fail;
+ }
+
+ /* Check if we're looking at a NVIDIA USB DAC device */
+ ret = ioctl(fd, HIDIOCGDEVINFO, &dev_info);
+ if (ret) {
+ perr("Failed to read device info for %s", filename);
+ goto fail;
+ }
+
+ if (dev_info.vendor != NVIDIA_VENDOR_ID ||
+ dev_info.product != USB_DAC_PRODUCT_ID) {
+ ret = EINVAL;
+ goto fail;
+ }
+
+ s->fd = fd;
+ return 0;
+
+fail:
+ if (fd >= 0) {
+ close(fd);
+ }
+
+ return ret;
+}
+
+static int find_hiddev(struct state *s) {
+ static char *dev_usb_dir = "/dev/usb";
+ struct dirent *dirent;
+ DIR *dir;
+
+ /* If the user picked a filename, just use that */
+ if (s->hiddev_filename)
+ return try_open_device(s, s->hiddev_filename);
+
+ /* Look for files /dev/usb/hiddev* */
+ dir = opendir(dev_usb_dir);
+ if (!dir) {
+ int err = errno;
+ perr("Failed to open %s", dev_usb_dir);
+ return err;
+ }
+
+ while ((dirent = readdir(dir))) {
+ char *filename = NULL;
+
+#if defined(_DIRENT_HAVE_D_TYPE)
+ if (dirent->d_type != DT_CHR)
+ continue;
+#endif
+
+ if (asprintf(&filename, "%s/%s", dev_usb_dir, dirent->d_name) == -1)
+ return ENOMEM;
+
+ if (!try_open_device(s, filename)) {
+ fprintf(stderr, "Found USB device at %s\n", filename);
+ s->hiddev_filename = filename;
+ return 0;
+ }
+
+ free(filename);
+ filename = NULL;
+ }
+
+ return ENOENT;
+}
+
+static void print_help(void) {
+ fprintf(stderr, "Usage: %s <options>\n\n", prog_name);
+ fprintf(stderr, "Valid options:\n");
+ fprintf(stderr, "--help, -h\n");
+ fprintf(stderr, " Print this help text and exit\n");
+ fprintf(stderr, "--hiddev_file <path>, -f <path>\n");
+ fprintf(stderr, " Manually specify the hiddev device file to open\n");
+ fprintf(stderr, " Default: autodetect\n");
+ fprintf(stderr, "--timestamps, -t\n");
+ fprintf(stderr, " Output timestamps. Default: off\n");
+ fprintf(stderr, "--short, -s\n");
+ fprintf(stderr, " Don't print strings describing the readings (useful "
+ "for scripting)\n");
+ fprintf(stderr, " Default: print the strings\n");
+}
+
+static int parse_options(struct state *s, int argc, char **argv) {
+ const struct option longopts[] = {
+ { .name = "help",
+ .val = 'h', },
+ { .name = "hiddev_file",
+ .has_arg = 1,
+ .val = 'f', },
+ { .name = "timestamps",
+ .val = 't', },
+ { .name = "short",
+ .val = 's', },
+ };
+ const char *shortopts = "f:hts";
+ char opt;
+
+ while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
+ switch (opt) {
+ case 'f':
+ s->hiddev_filename = strdup(optarg);
+ if (!s->hiddev_filename) {
+ return ENOMEM;
+ }
+ break;
+ case 't':
+ s->timestamps = 1;
+ break;
+ case 's':
+ s->no_names = 1;
+ break;
+ case 'h':
+ print_help();
+ exit(0);
+ case '?':
+ return EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ static struct state s;
+ prog_name = argv[0];
+
+ if (parse_options(&s, argc, argv)) {
+ print_help();
+ return 1;
+ }
+
+ if (find_hiddev(&s)) {
+ fprintf(stderr, "%s: Failed to find USB device, aborting\n", prog_name);
+ return 1;
+ }
+
+ if (read_data(&s)) {
+ fprintf(stderr, "%s: Failed to read data, aborting\n", prog_name);
+ return 1;
+ }
+
+ close(s.fd);
+
+ return 0;
+}
diff --git a/power_tools.h b/power_tools.h
new file mode 100644
index 0000000..0dc7803
--- /dev/null
+++ b/power_tools.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, NVIDIA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of NVIDIA nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __POWER_TOOLS_H__
+#define __POWER_TOOLS_H__
+
+struct probe_data {
+ const char *name;
+ /* The value of the resistor for differential readings */
+ double resistance;
+};
+
+#endif /* __POWER_TOOLS_H__ */
diff --git a/seaboard.h b/seaboard.h
new file mode 100644
index 0000000..19334ba
--- /dev/null
+++ b/seaboard.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010, NVIDIA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of NVIDIA nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SEABOARD_H__
+#define __SEABOARD_H__
+
+#include "power_tools.h"
+
+/* This is derived from the board schematics */
+static const struct probe_data seaboard_probes[] = {
+ { .name = "BAT", .resistance = 0.01, },
+ { .name = "SYS_SM1", .resistance = 0.02, },
+ { .name = "SYS_SM2", .resistance = 0.02, },
+ { .name = "CHRG", .resistance = 0.01, },
+ { .name = "DDR2", .resistance = 0.05, },
+ { .name = "SYS_SM0", .resistance = 0.05, },
+ { .name = "BL", .resistance = 0.02, },
+ { .name = "PNL", .resistance = 0.01, },
+};
+
+#endif /* __SEABOARD_H__ */