ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/devfreq/governor_throughput.c b/marvell/linux/drivers/devfreq/governor_throughput.c
new file mode 100644
index 0000000..855941e
--- /dev/null
+++ b/marvell/linux/drivers/devfreq/governor_throughput.c
@@ -0,0 +1,167 @@
+/*
+ *  linux/drivers/devfreq/governor_throughput.c
+ *
+ *  Copyright (C) 2013 Marvell
+ *	Leo Song <liangs@marvell.com>
+ *	Qiming Wu <wuqm@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/devfreq.h>
+#include <linux/math64.h>
+#include "governor.h"
+
+
+/* Default constants for DevFreq-Throughput */
+#define DFSO_UPTHRESHOLD	(90)
+#define DFSO_DOWNDIFFERENCTIAL	(5)
+#define DFSO_MAX_UPTHRESHOLD	(100)
+#define MAX_TABLE_ITEM		(20)
+
+static int devfreq_throughput_func(struct devfreq *df, unsigned long *freq)
+{
+	struct devfreq_throughput_data *data = df->data;
+	struct throughput_threshold *throughput_table = data->throughput_table;
+	u32 *freq_table = data->freq_table;
+	u32 table_len = data->table_len;
+	struct devfreq_dev_status stat;
+	int i;
+	int err;
+
+	/* only 1 freq point can not use this governor */
+	if ((data->table_len <= 1) || (data->table_len > MAX_TABLE_ITEM)) {
+		dev_err(&df->dev, "freq table too small or too big!\n");
+		return -EINVAL;
+	}
+
+	err = df->profile->get_dev_status(df->dev.parent, &stat);
+	if (err) {
+		/* Used to represent ignoring the profiling result */
+		if (err == -EINVAL) {
+			*freq = stat.current_frequency;
+			return 0;
+		} else
+			return err;
+	}
+
+	/*
+	 * make sure get_dev_status() has updated the current_frequency;
+	 * at least set *freq to a reansonable values
+	 */
+	*freq = stat.current_frequency;
+
+	/*
+	 * throughput == -1 is set by low level driver deliberately, to inform
+	 * the governor that the throughput data may not correct this time.
+	 * This is not fatal error, just end this time's caculation, and do it
+	 * next time.
+	 */
+	if (stat.throughput == -1) {
+		dev_dbg(&df->dev, "throughput is not suitable!\n");
+		return -EAGAIN;
+	} else if (stat.throughput < -1) {
+		dev_err(&df->dev, "fatal: throughput is not as expected!\n");
+		return -EINVAL;
+	}
+
+	if ((stat.throughput >= 0)
+	 && (stat.throughput < throughput_table[0].down)) {
+		/* set to lowest freq */
+		*freq = freq_table[0];
+	} else if (stat.throughput > throughput_table[table_len - 2].up) {
+		/* set to highest freq */
+		*freq = freq_table[table_len - 1];
+	} else {
+		for (i = 0; i <= (table_len - 2); i++) {
+			if ((stat.throughput >= throughput_table[i].down)
+			 && (stat.throughput <= throughput_table[i].up)) {
+				/*
+				 * If cur_speed is between UP and DOWN
+				 * keep freq unchanged in most cases
+				 */
+				*freq = stat.current_frequency;
+				/* Handle corner case */
+				if ((*freq != freq_table[i])
+				 && (*freq != freq_table[i + 1])) {
+					/* also can choose freq_table[i + 1] */
+					*freq = freq_table[i];
+				}
+				break;
+			} else if ((stat.throughput > throughput_table[i].up)
+			&& (stat.throughput < throughput_table[i + 1].down)) {
+				*freq = freq_table[i + 1];
+				break;
+			}
+		}
+	}
+
+	if (df->min_freq && *freq < df->min_freq)
+		*freq = df->min_freq;
+	if (df->max_freq && *freq > df->max_freq)
+		*freq = df->max_freq;
+
+	dev_dbg(&df->dev, "speed=%d, *freq=%lu\n", stat.throughput, *freq);
+
+	return 0;
+}
+
+static int devfreq_throughput_handler(struct devfreq *devfreq,
+				unsigned int event, void *data)
+{
+	switch (event) {
+	case DEVFREQ_GOV_START:
+		devfreq_monitor_start(devfreq);
+		break;
+
+	case DEVFREQ_GOV_STOP:
+		devfreq_monitor_stop(devfreq);
+		break;
+
+	case DEVFREQ_GOV_INTERVAL:
+		devfreq_interval_update(devfreq, (unsigned int *)data);
+		break;
+
+	case DEVFREQ_GOV_SUSPEND:
+		devfreq_monitor_suspend(devfreq);
+		break;
+
+	case DEVFREQ_GOV_RESUME:
+		devfreq_monitor_resume(devfreq);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static struct devfreq_governor devfreq_throughput = {
+	.name = "throughput",
+	.get_target_freq = devfreq_throughput_func,
+	.event_handler = devfreq_throughput_handler,
+};
+
+static int __init devfreq_throughput_init(void)
+{
+	return devfreq_add_governor(&devfreq_throughput);
+}
+subsys_initcall(devfreq_throughput_init);
+
+static void __exit devfreq_throughput_exit(void)
+{
+	int ret;
+
+	ret = devfreq_remove_governor(&devfreq_throughput);
+	if (ret)
+		pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+	return;
+}
+module_exit(devfreq_throughput_exit);
+MODULE_LICENSE("GPL");