Add support for Genpd.

This patch adds support to dump/display the powerdomain statistics
in powerdebug.

Signed-off-by: Thara Gopinath <thara.gopinath@linaro.org>
diff --git a/Makefile b/Makefile
index 2da9d67..4da3160 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
 CFLAGS?=-O1 -g -Wall -Wshadow
 CC?=gcc
 
-OBJS = powerdebug.o sensor.o clocks.o regulator.o gpio.o \
+OBJS = powerdebug.o sensor.o clocks.o regulator.o gpio.o genpd.o\
 	display.o tree.o utils.o mainloop.o
 
 default: powerdebug
diff --git a/display.c b/display.c
index 5665ed5..9cd3b9d 100644
--- a/display.c
+++ b/display.c
@@ -73,6 +73,7 @@
 	[REGULATOR] = { .name = "Regulators" },
 	[SENSOR]    = { .name = "Sensors"    },
 	[GPIO]      = { .name = "Gpio"    },
+	[GENPD]	    = { .name = "Powerdomains" },
 };
 
 static void display_fini(void)
diff --git a/display.h b/display.h
index 6083c53..36c2b52 100644
--- a/display.h
+++ b/display.h
@@ -19,7 +19,7 @@
  *
  */
 
-enum { CLOCK, REGULATOR, SENSOR, GPIO };
+enum { CLOCK, REGULATOR, SENSOR, GPIO, GENPD };
 
 struct display_ops {
 	int (*display)(bool refresh);
diff --git a/genpd.c b/genpd.c
new file mode 100644
index 0000000..7b69dbb
--- /dev/null
+++ b/genpd.c
@@ -0,0 +1,358 @@
+/*
+ * Powerdebug : power debugging tool
+ *
+ * Copyright (C) 2016, Linaro Limited.
+ *
+ * Author:
+ * Thara Gopinath <thara.gopinath@linaro.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+#define DEBUGFS_GENPD "/sys/kernel/debug/pm_genpd"
+#define NAME_MAX	16
+#define DEVICE_NAME_MAX	256
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#undef _GNU_SOURCE
+#include <sys/types.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "display.h"
+#include "powerdebug.h"
+#include "tree.h"
+#include "utils.h"
+
+struct genpd_idle_state {
+	char name[NAME_MAX];
+	long long idle_time;
+};
+
+struct genpd_info {
+	long long active_time;
+	long long total_idle_time;
+	char current_state[NAME_MAX];
+	struct genpd_idle_state *idle_states;
+	char (*devices)[DEVICE_NAME_MAX];
+	char (*sub_domains)[NAME_MAX];
+	int nr_devices;
+	int nr_subdomains;
+	int nr_states;
+};
+
+static struct tree *genpd_tree;
+
+static struct genpd_info *genpd_alloc(void)
+{
+	struct genpd_info *genpd;
+
+	genpd = malloc(sizeof(*genpd));
+	if (genpd)
+		memset(genpd, 0, sizeof(*genpd));
+
+	return genpd;
+}
+
+static int genpd_filter_cb(const char *name)
+{
+	/* Ignore the summary directory */
+	if (!strcmp(name, "pm_genpd_summary"))
+		return -1;
+	if (!strcmp(name, "pm_genpd"))
+		return -1;
+
+	return 0;
+}
+
+static int genpd_dump_cb(struct tree *t, void *data)
+{
+	struct genpd_info *genpd = t->private;
+	int i;
+
+	if (!t->parent)
+		return 0;
+
+	printf("\n%s:\n", t->name);
+	printf("current_state: %s\n", genpd->current_state);
+	printf("active_time: %lld ms\n", genpd->active_time);
+	printf("total_idle_time: %lld ms\n", genpd->total_idle_time);
+	printf("Idle States:\n");
+	for (i = 0; i < genpd->nr_states; i++) {
+		struct genpd_idle_state state = genpd->idle_states[i];
+
+		if (!i)
+			printf("%*s State %*s Time\n", 12, "", 10, "");
+		printf("%*s %-16s %lld\n", 12, "", state.name,
+							state.idle_time);
+	}
+	printf("Devices:\n");
+	for (i = 0; i < genpd->nr_devices; i++)
+		printf("%*s %s\n", 8, "", genpd->devices[i]);
+	printf("Subdomains:\n");
+	for (i = 0; i < genpd->nr_subdomains; i++)
+		printf("%*s %s\n", 11, "", genpd->sub_domains[i]);
+
+	return 0;
+}
+
+static int genpd_display_cb(struct tree *t, void *data)
+{
+	struct genpd_info *genpd = t->private;
+	int *line = data;
+	int nr_states = 0, nr_devices = 0, nr_domains = 0, i = 0;
+	char *buf;
+
+	if (!t->parent)
+		return 0;
+
+	while (1) {
+		char *state_buf, *device_buf, *domain_buf;
+
+		if ((i) && (nr_states == genpd->nr_states) &&
+			(nr_devices == genpd->nr_devices) &&
+			(nr_domains == genpd->nr_subdomains)) {
+			display_print_line(GENPD, *line, " ", 1, t);
+			(*line)++;
+			break;
+		}
+
+		if (nr_states < genpd->nr_states) {
+			if (asprintf(&state_buf, "%-10s %lld",
+				genpd->idle_states[nr_states].name,
+				genpd->idle_states[nr_states].idle_time) < 0)
+				return -1;
+			nr_states++;
+		} else {
+			if (asprintf(&state_buf, "%s", "") < 0)
+				return -1;
+		}
+
+		if (nr_devices < genpd->nr_devices) {
+			if (asprintf(&device_buf, "%s",
+				genpd->devices[nr_devices]) < 0)
+				return -1;
+			nr_devices++;
+		} else {
+			if (asprintf(&device_buf, "%s", "") < 0)
+				return -1;
+		}
+
+		if (nr_domains < genpd->nr_subdomains) {
+			if (asprintf(&domain_buf, "%s",
+				genpd->sub_domains[nr_domains]) < 0)
+				return -1;
+			nr_domains++;
+		} else {
+			if (asprintf(&domain_buf, "%s", "") < 0)
+				return -1;
+		}
+
+		if (!i) {
+			if (asprintf(&buf, "%-9s %-18s %-20lld %-24lld %-34s "
+				"%-50s %-15s", t->name, genpd->current_state,
+				genpd->active_time, genpd->total_idle_time,
+				state_buf, device_buf, domain_buf) < 0)
+				return -1;
+		} else {
+			if (asprintf(&buf, "%-74s %-34s %-50s %-15s",
+				"", state_buf, device_buf, domain_buf) < 0)
+				return -1;
+		}
+
+		display_print_line(GENPD, *line, buf, 1, t);
+		(*line)++;
+		free(buf);
+		free(state_buf);
+		free(device_buf);
+		free(domain_buf);
+		i++;
+	}
+
+	return 0;
+}
+
+static int genpd_print_header(void)
+{
+	char *buf;
+	int ret;
+
+	if (asprintf(&buf, "%-9s %-18s %-20s %-24s %-34s %-50s %-15s", "Name",
+		"Current State", "Active Time(ms)", "Total Idle Time(ms)",
+		"Idle States(State,Time ms)", "Devices", "Subdomains") < 0)
+		return -1;
+
+	ret = display_column_name(buf);
+	free(buf);
+
+	return ret;
+}
+
+static int genpd_print_info(struct tree *t)
+{
+	int ret, line = 0;
+
+	display_reset_cursor(GENPD);
+
+	genpd_print_header();
+
+	ret = tree_for_each(t, genpd_display_cb, &line);
+
+	display_refresh_pad(GENPD);
+
+	return ret;
+}
+
+static int read_genpd_cb(struct tree *t, void *data)
+{
+	struct genpd_info *genpd = t->private;
+	FILE *fp;
+	char line[256];
+	int nr_states = 0, nr_devices = 0, nr_sub_domains = 0;
+
+	file_read_value(t->path, "active_time", "%lld", &genpd->active_time);
+	file_read_value(t->path, "total_idle_time", "%lld",
+						&genpd->total_idle_time);
+	file_read_value(t->path, "current_state", "%s", &genpd->current_state);
+	file_open(&fp, t->path, "idle_states", "r");
+	while (!(file_read_line(&fp, line, sizeof(line)))) {
+		if (!strncmp(line, "State", 5))
+			continue;
+
+		genpd->idle_states = realloc(genpd->idle_states,
+			sizeof(struct genpd_idle_state) * (nr_states + 1));
+		if (!genpd->idle_states)
+			continue;
+		sscanf(line, "%s %lld", genpd->idle_states[nr_states].name,
+				&(genpd->idle_states[nr_states].idle_time));
+
+		nr_states++;
+	}
+	file_close(&fp);
+
+	file_open(&fp, t->path, "devices", "r");
+	while (!(file_read_line(&fp, line, sizeof(line)))) {
+		int len;
+
+		genpd->devices = realloc(genpd->devices,
+			sizeof(*(genpd->devices)) * (nr_devices + 1));
+		if (!genpd->devices)
+			continue;
+
+		len = strlen(line);
+		line[len - 1] = '\0';
+		strcpy(genpd->devices[nr_devices], line);
+		nr_devices++;
+	}
+	file_close(&fp);
+
+	file_open(&fp, t->path, "sub_domains", "r");
+	while (!(file_read_line(&fp, line, sizeof(line)))) {
+		int len;
+
+		genpd->sub_domains = realloc(genpd->sub_domains,
+			sizeof(*(genpd->sub_domains)) * (nr_sub_domains + 1));
+		if (!genpd->sub_domains)
+			continue;
+
+		len = strlen(line);
+		line[len - 1] = '\0';
+		strcpy(genpd->sub_domains[nr_sub_domains], line);
+		nr_sub_domains++;
+	}
+
+	genpd->nr_states = nr_states;
+	genpd->nr_devices = nr_devices;
+	genpd->nr_subdomains = nr_sub_domains;
+
+	return 0;
+}
+
+static int read_genpd_info(struct tree *t)
+{
+	return tree_for_each(t, read_genpd_cb, NULL);
+}
+
+static int fill_genpd_cb(struct tree *t, void *data)
+{
+	struct genpd_info *genpd;
+
+	genpd = genpd_alloc();
+	if (!genpd) {
+		printf("error: unable to allocate memory for genpd\n");
+		return -1;
+	}
+
+	t->private = genpd;
+
+	return read_genpd_cb(t, data);
+}
+
+static int fill_genpd_tree(void)
+{
+	return tree_for_each(genpd_tree, fill_genpd_cb, NULL);
+}
+
+static int genpd_info_load(void)
+{
+	if (genpd_tree)
+		return 0;
+
+	genpd_tree = tree_load(DEBUGFS_GENPD, genpd_filter_cb, false);
+	if (!genpd_tree)
+		return -1;
+
+	if (fill_genpd_tree())
+		return -1;
+
+	return 0;
+}
+
+int genpd_dump(void)
+{
+	if (!genpd_tree)
+		genpd_info_load();
+	else
+		read_genpd_info(genpd_tree);
+
+	return tree_for_each(genpd_tree, genpd_dump_cb, NULL);
+
+	return 0;
+}
+
+static int genpd_display(bool refresh)
+{
+	if (genpd_info_load()) {
+		display_print_error(GENPD, 0, "Failed to read genpd info");
+		return 0;
+	}
+
+	if (refresh && read_genpd_info(genpd_tree))
+		return -1;
+
+	return genpd_print_info(genpd_tree);
+}
+
+static struct display_ops genpd_ops = {
+	.display = genpd_display,
+};
+
+int genpd_init(struct powerdebug_options *options)
+{
+	if (!(options->flags & GENPD_OPTION))
+		return 0;
+
+	return display_register(GENPD, &genpd_ops);
+}
+
diff --git a/powerdebug.c b/powerdebug.c
index 188c91d..e18670e 100644
--- a/powerdebug.c
+++ b/powerdebug.c
@@ -35,14 +35,15 @@
 	printf("Usage: powerdebug [OPTIONS]\n");
 	printf("\n");
 	printf("powerdebug -d [ -r ] [ -s ] [ -c [ -f <clock-name> ] ] "
-		"[ -g ] [ -v ]\n");
-	printf("powerdebug [ -r | -s | -c | -g]\n");
+		"[ -g ] [ -p ] [ -v ]\n");
+	printf("powerdebug [ -r | -s | -c | -g | -p]\n");
 	printf("  -r, --regulator 	Show regulator information\n");
 	printf("  -s, --sensor		Show sensor information\n");
 	printf("  -c, --clock		Show clock information\n");
 	printf("  -f, --findparents	Show all parents for a particular"
 		" clock\n");
 	printf("  -g, --gpio		Show gpio information\n");
+	printf("  -p, --powerdomain	Show powerdomain information\n");
 	printf("  -t, --time		Set ticktime in seconds (eg. 10.0)\n");
 	printf("  -d, --dump		Dump information once (no refresh)\n");
 	printf("  -v, --verbose		Verbose mode (use with -r and/or"
@@ -62,6 +63,7 @@
  * -s, --sensor	 	: sensors
  * -c, --clock	  	: clocks
  * -g, --gpio           : gpios
+ * -p, --powerdomain	: powerdomains
  * -f, --findparents    : clockname whose parents have to be found
  * -t, --time		: ticktime
  * -d, --dump		: dump
@@ -76,6 +78,7 @@
 	{ "sensor", 0, 0, 's' },
 	{ "clock",  0, 0, 'c' },
 	{ "gpio",  0, 0, 'g' },
+	{ "powerdomain", 0, 0, 'p' },
 	{ "findparents", 1, 0, 'f' },
 	{ "time", 1, 0, 't' },
 	{ "dump", 0, 0, 'd' },
@@ -95,7 +98,7 @@
 	while (1) {
 		int optindex = 0;
 
-		c = getopt_long(argc, argv, "rscgf:t:dvVh",
+		c = getopt_long(argc, argv, "rscgpf:t:dvVh",
 				long_options, &optindex);
 		if (c == -1)
 			break;
@@ -113,6 +116,9 @@
 		case 'g':
 			options->flags |= GPIO_OPTION;
 			break;
+		case 'p':
+			options->flags |= GENPD_OPTION;
+			break;
 		case 'd':
 			options->flags |= DUMP_OPTION;
 			break;
@@ -162,6 +168,9 @@
 	if (options->flags & GPIO_OPTION)
 		gpio_dump();
 
+	if (options->flags & GENPD_OPTION)
+		genpd_dump();
+
 	return 0;
 }
 
@@ -240,6 +249,11 @@
 		options->flags &= GPIO_OPTION;
 	}
 
+	if (genpd_init(options)) {
+		printf("failed to initialize genpd details\n");
+		options->flags &= GENPD_OPTION;
+	}
+
 	ret = options->flags & DUMP_OPTION ? powerdebug_dump(options) :
 		powerdebug_display(options);
 
diff --git a/powerdebug.h b/powerdebug.h
index a8073a6..992c5c0 100644
--- a/powerdebug.h
+++ b/powerdebug.h
@@ -25,12 +25,12 @@
 #define REGULATOR_OPTION	0x02
 #define SENSOR_OPTION		0x04
 #define GPIO_OPTION		0x08
-
+#define GENPD_OPTION		0x10
 #define VERBOSE_OPTION		0x1000
 #define DUMP_OPTION		0x2000
 
 #define DEFAULT_OPTION (REGULATOR_OPTION | SENSOR_OPTION | \
-			CLOCK_OPTION | GPIO_OPTION)
+			CLOCK_OPTION | GPIO_OPTION | GENPD_OPTION)
 
 struct powerdebug_options {
 	int flags;
@@ -50,3 +50,5 @@
 extern int sensor_init(struct powerdebug_options *options);
 extern int sensor_dump(void);
 
+extern int genpd_init(struct powerdebug_options *options);
+extern int genpd_dump(void);