msm: dcvs: create cpu freq table dynamically

CPU frequencies can differ between different revisions, so create
the table of cpu frequencies dynamically based on data from
acpuclock rather than using a fixed table.

Some of the data in the table comes from sync points that will be
implemented between CPU and GPU frequencies. The sync points are
defined here, but the sync implementation itself will come in a
subsequent commit.

Change-Id: I24cf17b199df1e5d064c1894b269e49118b39ac7
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
(cherry picked from commit f9a8749b17961be653e9cbb426b1b72cada7f61c)
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index 7b5aa23..bfb3df3 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -33,6 +33,7 @@
 #include <mach/rpm-regulator.h>
 #include <mach/rpm-regulator-smd.h>
 #include <mach/msm_bus.h>
+#include <mach/msm_dcvs.h>
 
 #include "acpuclock.h"
 #include "acpuclock-krait.h"
@@ -883,6 +884,17 @@
 static void __init cpufreq_table_init(void) {}
 #endif
 
+static void __init dcvs_freq_init(void)
+{
+	int i;
+
+	for (i = 0; drv.acpu_freq_tbl[i].speed.khz != 0; i++)
+		if (drv.acpu_freq_tbl[i].use_for_scaling)
+			msm_dcvs_register_cpu_freq(
+				drv.acpu_freq_tbl[i].speed.khz,
+				drv.acpu_freq_tbl[i].vdd_core / 1000);
+}
+
 static int __cpuinit acpuclk_cpu_callback(struct notifier_block *nfb,
 					    unsigned long action, void *hcpu)
 {
@@ -1088,6 +1100,7 @@
 	hw_init();
 
 	cpufreq_table_init();
+	dcvs_freq_init();
 	acpuclk_register(&acpuclk_krait_data);
 	register_hotcpu_notifier(&acpuclk_cpu_notifier);
 
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index e762180..2f2bf38 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2328,6 +2328,7 @@
 	&msm_pil_vidc,
 	&msm_gss,
 	&apq8064_rtb_device,
+	&apq8064_dcvs_device,
 	&apq8064_msm_gov_device,
 	&apq8064_device_cache_erp,
 	&msm8960_device_ebi1_ch0_erp,
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 83d756c..44620a0 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2623,18 +2623,26 @@
 	.resource	= i2s_mdm_resources,
 };
 
-static struct msm_dcvs_freq_entry apq8064_freq[] = {
-	{ 384000, 900,  0, 0, 0},
-	{ 594000, 950,  0, 0, 0},
-	{ 702000, 975,  0, 0, 0},
-	{1026000, 1075, 0, 0, 0},
-	{1242000, 1150, 0, 100, 100},
-	{1458000, 1188, 0, 100, 100},
-	{1512000, 1200, 1, 100, 100},
+static struct msm_dcvs_sync_rule apq8064_dcvs_sync_rules[] = {
+	{1026000,	400000},
+	{384000,	200000},
+	{-1,		128000},
+};
+
+static struct msm_dcvs_platform_data apq8064_dcvs_data = {
+	.sync_rules	= apq8064_dcvs_sync_rules,
+	.num_sync_rules = ARRAY_SIZE(apq8064_dcvs_sync_rules),
+};
+
+struct platform_device apq8064_dcvs_device = {
+	.name		= "dcvs",
+	.id		= -1,
+	.dev		= {
+		.platform_data = &apq8064_dcvs_data,
+	},
 };
 
 static struct msm_dcvs_core_info apq8064_core_info = {
-	.freq_tbl		= &apq8064_freq[0],
 	.num_cores		= 4,
 	.sensors		= (int[]){7, 8, 9, 10},
 	.thermal_poll_ms	= 60000,
@@ -2669,7 +2677,7 @@
 	},
 	.power_param		= {
 		.current_temp	= 25,
-		.num_freq	= ARRAY_SIZE(apq8064_freq),
+		.num_freq	= 0, /* set at runtime */
 	}
 };
 
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 0ac836c..12be2b7 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -395,6 +395,7 @@
 extern struct platform_device *msm_8974_stub_regulator_devices[];
 extern int msm_8974_stub_regulator_devices_len;
 
+extern struct platform_device apq8064_dcvs_device;
 extern struct platform_device apq8064_msm_gov_device;
 
 extern struct platform_device msm_bus_8930_apps_fabric;
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index e81cee4..1ca5a32 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -36,12 +36,36 @@
 	MSM_DCVS_DISABLE_HIGH_LATENCY_MODES,
 };
 
+struct msm_dcvs_sync_rule {
+	unsigned long cpu_khz;
+	unsigned long gpu_floor_khz;
+};
+
+struct msm_dcvs_platform_data {
+	struct msm_dcvs_sync_rule *sync_rules;
+	unsigned num_sync_rules;
+};
+
 struct msm_gov_platform_data {
 	struct msm_dcvs_core_info *info;
 	int latency;
 };
 
 /**
+ * msm_dcvs_register_cpu_freq
+ * @freq: the frequency value to register
+ * @voltage: the operating voltage (in mV) associated with the above frequency
+ *
+ * Register a cpu frequency and its operating voltage with dcvs.
+ */
+#ifdef CONFIG_MSM_DCVS
+void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage);
+#else
+static inline void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
+{}
+#endif
+
+/**
  * msm_dcvs_idle
  * @dcvs_core_id: The id returned by msm_dcvs_register_core
  * @state: The enter/exit idle state the core is in
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index c1c05af..358027c 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -23,6 +23,7 @@
 #include <linux/stringify.h>
 #include <linux/debugfs.h>
 #include <linux/msm_tsens.h>
+#include <linux/platform_device.h>
 #include <asm/atomic.h>
 #include <asm/page.h>
 #include <mach/msm_dcvs.h>
@@ -140,6 +141,11 @@
 
 static struct kobject *cores_kobj;
 
+#define DCVS_MAX_NUM_FREQS 15
+static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
+static unsigned num_cpu_freqs;
+static struct msm_dcvs_platform_data *dcvs_pdata;
+
 static void force_stop_slack_timer(struct dcvs_core *core)
 {
 	unsigned long flags;
@@ -825,6 +831,37 @@
 	return &core_list[offset];
 }
 
+void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
+{
+	BUG_ON(freq == 0 || voltage == 0 ||
+	       num_cpu_freqs == DCVS_MAX_NUM_FREQS);
+
+	cpu_freq_tbl[num_cpu_freqs].freq = freq;
+	cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
+
+	num_cpu_freqs++;
+}
+
+static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
+{
+	int i;
+
+	BUG_ON(num_cpu_freqs == 0);
+
+	info->freq_tbl = cpu_freq_tbl;
+	info->power_param.num_freq = num_cpu_freqs;
+
+	if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
+		return;
+
+	/* the first sync rule shows what the turbo frequencies are -
+	 * these frequencies need energy offsets set */
+	for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
+		if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
+			cpu_freq_tbl[i].active_energy_offset = 100;
+			cpu_freq_tbl[i].leakage_energy_offset = 100;
+		}
+}
 
 int msm_dcvs_register_core(
 	enum msm_dcvs_core_type type,
@@ -860,6 +897,9 @@
 	core->pending_freq = STOP_FREQ_CHANGE;
 
 	core->info = info;
+	if (type == MSM_DCVS_CORE_TYPE_CPU)
+		update_cpu_dcvs_params(info);
+
 	memcpy(&core->algo_param, &info->algo_param,
 			sizeof(struct msm_dcvs_algo_param));
 
@@ -1109,11 +1149,29 @@
 }
 late_initcall(msm_dcvs_late_init);
 
+static int __devinit dcvs_probe(struct platform_device *pdev)
+{
+	if (pdev->dev.platform_data)
+		dcvs_pdata = pdev->dev.platform_data;
+
+	return 0;
+}
+
+static struct platform_driver dcvs_driver = {
+	.probe = dcvs_probe,
+	.driver = {
+		.name = "dcvs",
+		.owner = THIS_MODULE,
+	},
+};
+
 static int __init msm_dcvs_early_init(void)
 {
 	int ret = 0;
 	int i;
 
+	platform_driver_register(&dcvs_driver);
+
 	if (!msm_dcvs_enabled) {
 		__info("Not enabled (%d)\n", msm_dcvs_enabled);
 		return 0;