msm: dcvs: add thermal control.

Add capability to read the sensor data and report the temperature of
a core to the TZ.

Note that this was done earlier whenever a frequency update was done,
instead create a delayed workqueue per core. Also create a attribute
that controls the rate of thermal update per core.

If that attribute is not set use 60seconds as the default update rate.
If it is less than one second, default to using one second.

Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
(cherry picked from commit 80add4203ec4538046ca926e2747db6d01c14783)

Signed-off-by: Ram Kumar Chakravarthy Chebathini <rcheba@codeaurora.org>
(cherry picked from commit 2155283a8adf2c8b9dd27249f9c07a313a6ff710)

Change-Id: If91343b975d490c05ea63c9977d81f9572072773
Signed-off-by: Sudhir Sharma <sudsha@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index 940517f..ec633a2 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -31,13 +31,14 @@
 };
 
 static struct msm_dcvs_core_info grp3d_core_info = {
-	.freq_tbl	= &grp3d_freq[0],
-	.num_cores	= 1,
-	.sensors	= (int[]){0},
-	.core_param	= {
+	.freq_tbl		= &grp3d_freq[0],
+	.num_cores		= 1,
+	.sensors		= (int[]){0},
+	.thermal_poll_ms	= 60000,
+	.core_param		= {
 		.core_type	= MSM_DCVS_CORE_TYPE_GPU,
 	},
-	.algo_param	= {
+	.algo_param		= {
 		.disable_pc_threshold	= 0,
 		.em_win_size_min_us	= 100000,
 		.em_win_size_max_us	= 300000,
@@ -53,8 +54,7 @@
 		.ss_iobusy_conv		= 100,
 	},
 
-
-	.energy_coeffs	= {
+	.energy_coeffs		= {
 		.leakage_coeff_a	= -17720,
 		.leakage_coeff_b	= 37,
 		.leakage_coeff_c	= 3329,
@@ -65,7 +65,7 @@
 		.active_coeff_c		= 0
 	},
 
-	.power_param	= {
+	.power_param		= {
 		.current_temp	= 25,
 		.num_freq	= ARRAY_SIZE(grp3d_freq),
 	}
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 1d1b6d3..1b56860 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2600,13 +2600,14 @@
 };
 
 static struct msm_dcvs_core_info apq8064_core_info = {
-	.freq_tbl	= &apq8064_freq[0],
-	.num_cores	= 4,
-	.sensors	= (int[]){7, 8, 9, 10},
-	.core_param	= {
+	.freq_tbl		= &apq8064_freq[0],
+	.num_cores		= 4,
+	.sensors		= (int[]){7, 8, 9, 10},
+	.thermal_poll_ms	= 60000,
+	.core_param		= {
 		.core_type	= MSM_DCVS_CORE_TYPE_CPU,
 	},
-	.algo_param	= {
+	.algo_param		= {
 		.disable_pc_threshold		= 1458000,
 		.em_win_size_min_us		= 100000,
 		.em_win_size_max_us		= 300000,
@@ -2622,7 +2623,7 @@
 		.ss_win_size_max_us		= 1000000,
 		.ss_util_pct			= 95,
 	},
-	.energy_coeffs	= {
+	.energy_coeffs		= {
 		.active_coeff_a		= 336,
 		.active_coeff_b		= 0,
 		.active_coeff_c		= 0,
@@ -2632,7 +2633,7 @@
 		.leakage_coeff_c	= 3329,
 		.leakage_coeff_d	= -277,
 	},
-	.power_param	= {
+	.power_param		= {
 		.current_temp	= 25,
 		.num_freq	= ARRAY_SIZE(apq8064_freq),
 	}
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index 9b0d0c9..e81cee4 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -67,6 +67,7 @@
 struct msm_dcvs_core_info {
 	int					num_cores;
 	int					*sensors;
+	int					thermal_poll_ms;
 	struct msm_dcvs_freq_entry		*freq_tbl;
 	struct msm_dcvs_core_param		core_param;
 	struct msm_dcvs_algo_param		algo_param;
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index aadf08f..2386a05 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -62,6 +62,8 @@
 	struct kobj_attribute leakage_coeff_c;
 	struct kobj_attribute leakage_coeff_d;
 
+	struct kobj_attribute thermal_poll_ms;
+
 	struct attribute_group attrib_group;
 };
 
@@ -127,6 +129,7 @@
 	int pending_freq;
 
 	struct hrtimer	slack_timer;
+	struct delayed_work	temperature_work;
 };
 
 static int msm_dcvs_enabled = 1;
@@ -317,26 +320,43 @@
 	return ret;
 }
 
-static int __msm_dcvs_report_temp(struct dcvs_core *core)
+static void msm_dcvs_report_temp_work(struct work_struct *work)
 {
+	struct dcvs_core *core = container_of(work,
+					struct dcvs_core,
+					temperature_work.work);
 	struct msm_dcvs_core_info *info = core->info;
 	struct tsens_device tsens_dev;
 	int ret;
 	unsigned long temp = 0;
+	int interval_ms;
 
 	tsens_dev.sensor_num = core->sensor;
 	ret = tsens_get_temp(&tsens_dev, &temp);
-	if (!ret) {
+	if (!temp) {
 		tsens_dev.sensor_num = 0;
 		ret = tsens_get_temp(&tsens_dev, &temp);
-		if (!ret)
-			return -ENODEV;
+		if (!temp)
+			goto out;
 	}
 
+	if (temp == info->power_param.current_temp)
+		goto out;
+	info->power_param.current_temp = temp;
+
 	ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
 			&info->power_param,
 			&info->freq_tbl[0], &core->coeffs);
-	return ret;
+out:
+	if (info->thermal_poll_ms == 0)
+		interval_ms = 60000;
+	else if (info->thermal_poll_ms < 1000)
+		interval_ms = 1000;
+	else
+		interval_ms = info->thermal_poll_ms;
+
+	schedule_delayed_work(&core->temperature_work,
+			msecs_to_jiffies(interval_ms));
 }
 
 static int msm_dcvs_do_freq(void *data)
@@ -355,7 +375,6 @@
 			break;
 
 		__msm_dcvs_change_freq(core);
-		__msm_dcvs_report_temp(core);
 	}
 
 	return 0;
@@ -465,6 +484,28 @@
 	return snprintf(buf, PAGE_SIZE, "%d\n", v); \
 }
 
+#define DCVS_PARAM_STORE(_name) \
+static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
+		struct kobj_attribute *attr, char *buf) \
+{ \
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+	return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
+} \
+static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
+		struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+	int ret = 0; \
+	uint32_t val = 0; \
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+	ret = kstrtouint(buf, 10, &val); \
+	if (ret) { \
+		__err("Invalid input %s for %s\n", buf, __stringify(_name));\
+	} else { \
+		core->info->_name = val; \
+	} \
+	return count; \
+}
+
 #define DCVS_ALGO_PARAM(_name) \
 static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
 		struct kobj_attribute *attr, char *buf) \
@@ -573,11 +614,13 @@
 DCVS_ENERGY_PARAM(leakage_coeff_c)
 DCVS_ENERGY_PARAM(leakage_coeff_d)
 
+DCVS_PARAM_STORE(thermal_poll_ms)
+
 static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
 {
 	int ret = 0;
 	struct kobject *core_kobj = NULL;
-	const int attr_count = 27;
+	const int attr_count = 28;
 
 	BUG_ON(!cores_kobj);
 
@@ -618,7 +661,8 @@
 	DCVS_RW_ATTRIB(24, leakage_coeff_c);
 	DCVS_RW_ATTRIB(25, leakage_coeff_d);
 
-	core->attrib.attrib_group.attrs[26] = NULL;
+	DCVS_RW_ATTRIB(26, thermal_poll_ms);
+	core->attrib.attrib_group.attrs[27] = NULL;
 
 	core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
 	if (!core_kobj) {
@@ -762,6 +806,10 @@
 	core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
 			"msm_dcvs/%d", core->dcvs_core_id);
 	ret = core->dcvs_core_id;
+
+	INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
+	schedule_delayed_work(&core->temperature_work,
+			msecs_to_jiffies(info->thermal_poll_ms));
 	return ret;
 bail:
 	core->dcvs_core_id = -1;