PM / devfreq: memlat: Add a core to memory frequency mapping table

Add a core to memory frequency mapping table, which establishes
a relationship between the core frequency and its corresponding
bandwidth vote.

The governor expects a "qcom,core-dev-table" table as part of a given
memlat hardware monitor's device tree node.

This table is read upon registration of the memlat governor. The table
is then used to determine the memory bandwidth vote corresponding to the
maximum of the core frequencies.

CRs-Fixed: 1054146
Change-Id: I9df118da1433125b02c937bf1799a0944b110fac
Signed-off-by: Rohit Gupta <rohgup@codeaurora.org>
Signed-off-by: David Keitel <dkeitel@codeaurora.org>
Suggested-by: Saravana Kannan <skannan@codeaurora.org>
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index f22337b..53c6b89 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -35,8 +35,6 @@
 
 struct memlat_node {
 	unsigned int ratio_ceil;
-	unsigned int freq_thresh_mhz;
-	unsigned int mult_factor;
 	bool mon_started;
 	struct list_head list;
 	void *orig_data;
@@ -83,45 +81,25 @@
 store_attr(__attr, min, max)		\
 static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr)
 
-static unsigned long compute_dev_vote(struct devfreq *df)
+static unsigned long core_to_dev_freq(struct memlat_node *node,
+		unsigned long coref)
 {
-	int i, lat_dev;
-	struct memlat_node *node = df->data;
 	struct memlat_hwmon *hw = node->hw;
-	unsigned long max_freq = 0;
-	unsigned int ratio;
+	struct core_dev_map *map = hw->freq_map;
+	unsigned long freq = 0;
 
-	hw->get_cnt(hw);
+	if (!map)
+		goto out;
 
-	for (i = 0; i < hw->num_cores; i++) {
-		ratio = hw->core_stats[i].inst_count;
+	while (map->core_mhz && map->core_mhz < coref)
+		map++;
+	if (!map->core_mhz)
+		map--;
+	freq = map->target_freq;
 
-		if (hw->core_stats[i].mem_count)
-			ratio /= hw->core_stats[i].mem_count;
-
-		trace_memlat_dev_meas(dev_name(df->dev.parent),
-					hw->core_stats[i].id,
-					hw->core_stats[i].inst_count,
-					hw->core_stats[i].mem_count,
-					hw->core_stats[i].freq, ratio);
-
-		if (ratio && ratio <= node->ratio_ceil
-		    && hw->core_stats[i].freq >= node->freq_thresh_mhz
-		    && hw->core_stats[i].freq > max_freq) {
-			lat_dev = i;
-			max_freq = hw->core_stats[i].freq;
-		}
-	}
-
-	if (max_freq)
-		trace_memlat_dev_update(dev_name(df->dev.parent),
-					hw->core_stats[lat_dev].id,
-					hw->core_stats[lat_dev].inst_count,
-					hw->core_stats[lat_dev].mem_count,
-					hw->core_stats[lat_dev].freq,
-					max_freq * node->mult_factor);
-
-	return max_freq;
+out:
+	pr_debug("freq: %lu -> dev: %lu\n", coref, freq);
+	return freq;
 }
 
 static struct memlat_node *find_memlat_node(struct devfreq *df)
@@ -223,23 +201,51 @@
 static int devfreq_memlat_get_freq(struct devfreq *df,
 					unsigned long *freq)
 {
-	unsigned long mhz;
+	int i, lat_dev;
 	struct memlat_node *node = df->data;
+	struct memlat_hwmon *hw = node->hw;
+	unsigned long max_freq = 0;
+	unsigned int ratio;
 
-	mhz = compute_dev_vote(df);
-	*freq = mhz ? (mhz * node->mult_factor) : 0;
+	hw->get_cnt(hw);
 
+	for (i = 0; i < hw->num_cores; i++) {
+		ratio = hw->core_stats[i].inst_count;
+
+		if (hw->core_stats[i].mem_count)
+			ratio /= hw->core_stats[i].mem_count;
+
+		trace_memlat_dev_meas(dev_name(df->dev.parent),
+					hw->core_stats[i].id,
+					hw->core_stats[i].inst_count,
+					hw->core_stats[i].mem_count,
+					hw->core_stats[i].freq, ratio);
+
+		if (ratio && ratio <= node->ratio_ceil
+		    && hw->core_stats[i].freq > max_freq) {
+			lat_dev = i;
+			max_freq = hw->core_stats[i].freq;
+		}
+	}
+
+	if (max_freq) {
+		max_freq = core_to_dev_freq(node, max_freq);
+		trace_memlat_dev_update(dev_name(df->dev.parent),
+					hw->core_stats[lat_dev].id,
+					hw->core_stats[lat_dev].inst_count,
+					hw->core_stats[lat_dev].mem_count,
+					hw->core_stats[lat_dev].freq,
+					max_freq);
+	}
+
+	*freq = max_freq;
 	return 0;
 }
 
-gov_attr(ratio_ceil, 1U, 1000U);
-gov_attr(freq_thresh_mhz, 300U, 5000U);
-gov_attr(mult_factor, 1U, 10U);
+gov_attr(ratio_ceil, 1U, 10000U);
 
 static struct attribute *dev_attr[] = {
 	&dev_attr_ratio_ceil.attr,
-	&dev_attr_freq_thresh_mhz.attr,
-	&dev_attr_mult_factor.attr,
 	NULL,
 };
 
@@ -294,6 +300,48 @@
 	.event_handler = devfreq_memlat_ev_handler,
 };
 
+#define NUM_COLS	2
+static struct core_dev_map *init_core_dev_map(struct device *dev,
+		char *prop_name)
+{
+	int len, nf, i, j;
+	u32 data;
+	struct core_dev_map *tbl;
+	int ret;
+
+	if (!of_find_property(dev->of_node, prop_name, &len))
+		return NULL;
+	len /= sizeof(data);
+
+	if (len % NUM_COLS || len == 0)
+		return NULL;
+	nf = len / NUM_COLS;
+
+	tbl = devm_kzalloc(dev, (nf + 1) * sizeof(struct core_dev_map),
+			GFP_KERNEL);
+	if (!tbl)
+		return NULL;
+
+	for (i = 0, j = 0; i < nf; i++, j += 2) {
+		ret = of_property_read_u32_index(dev->of_node, prop_name, j,
+				&data);
+		if (ret)
+			return NULL;
+		tbl[i].core_mhz = data / 1000;
+
+		ret = of_property_read_u32_index(dev->of_node, prop_name, j + 1,
+				&data);
+		if (ret)
+			return NULL;
+		tbl[i].target_freq = data;
+		pr_debug("Entry%d CPU:%u, Dev:%u\n", i, tbl[i].core_mhz,
+				tbl[i].target_freq);
+	}
+	tbl[i].core_mhz = 0;
+
+	return tbl;
+}
+
 int register_memlat(struct device *dev, struct memlat_hwmon *hw)
 {
 	int ret = 0;
@@ -310,10 +358,14 @@
 	node->attr_grp = &dev_attr_group;
 
 	node->ratio_ceil = 10;
-	node->freq_thresh_mhz = 900;
-	node->mult_factor = 8;
 	node->hw = hw;
 
+	hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table");
+	if (!hw->freq_map) {
+		dev_err(dev, "Couldn't find the core-dev freq table!\n");
+		return -EINVAL;
+	}
+
 	mutex_lock(&list_lock);
 	list_add_tail(&node->list, &memlat_list);
 	mutex_unlock(&list_lock);
diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h
index 19a3500..a0e52a0 100644
--- a/drivers/devfreq/governor_memlat.h
+++ b/drivers/devfreq/governor_memlat.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -31,6 +31,11 @@
 	unsigned long freq;
 };
 
+struct core_dev_map {
+	unsigned int core_mhz;
+	unsigned int target_freq;
+};
+
 /**
  * struct memlat_hwmon - Memory Latency HW monitor info
  * @start_hwmon:		Start the HW monitoring
@@ -63,6 +68,7 @@
 	struct dev_stats *core_stats;
 
 	struct devfreq *df;
+	struct core_dev_map *freq_map;
 };
 
 #ifdef CONFIG_DEVFREQ_GOV_MEMLAT