thermal: msm8960_tsens: Add APQ8064 support

TSENS is used by the thermal daemon for thermal management.
On APQ8064 there are 11 TSENS sensors that can be used by the
thermal daemon to monitor the temperature across the chip.

TSENS for APQ8064 supports individual slope for each of the
sensors. The offset used in the temperature from each of the
slope is used for temperature calcuation from the ADC code.

Change-Id: I00457aff8d67ab3367882ffe1077af863b90bc49
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index 2a2166a..12edb3e 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/msm_tsens.h>
 #include <linux/io.h>
+#include <linux/err.h>
 
 #include <mach/msm_iomap.h>
 #include <mach/socinfo.h>
@@ -56,10 +57,12 @@
 #define SENSOR4_EN					BIT(7)
 #define SENSORS_EN			(SENSOR0_EN | SENSOR1_EN |	\
 					SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
-#define TSENS_MIN_STATUS_MASK				BIT(8)
-#define TSENS_LOWER_STATUS_CLR				BIT(9)
-#define TSENS_UPPER_STATUS_CLR				BIT(10)
-#define TSENS_MAX_STATUS_MASK				BIT(11)
+#define TSENS_STATUS_CNTL_OFFSET	8
+#define TSENS_MIN_STATUS_MASK		BIT((tsens_status_cntl_start))
+#define TSENS_LOWER_STATUS_CLR		BIT((tsens_status_cntl_start + 1))
+#define TSENS_UPPER_STATUS_CLR		BIT((tsens_status_cntl_start + 2))
+#define TSENS_MAX_STATUS_MASK		BIT((tsens_status_cntl_start + 3))
+
 #define TSENS_MEASURE_PERIOD				4 /* 1 sec. default */
 #define TSENS_8960_SLP_CLK_ENA				BIT(26)
 
@@ -86,6 +89,7 @@
 
 #define TSENS_S0_STATUS_ADDR			(MSM_CLK_CTL_BASE + 0x00003628)
 #define TSENS_STATUS_ADDR_OFFSET			2
+#define TSENS_SENSOR_STATUS_SIZE			4
 #define TSENS_INT_STATUS_ADDR			(MSM_CLK_CTL_BASE + 0x0000363c)
 
 #define TSENS_LOWER_INT_MASK				BIT(1)
@@ -113,6 +117,26 @@
 #define TSENS_8660_CONFIG_MASK			(3 << TSENS_8660_CONFIG_SHIFT)
 #define TSENS_8660_SLP_CLK_ENA				BIT(24)
 
+#define TSENS_8064_SENSOR5_EN				BIT(8)
+#define TSENS_8064_SENSOR6_EN				BIT(9)
+#define TSENS_8064_SENSOR7_EN				BIT(10)
+#define TSENS_8064_SENSOR8_EN				BIT(11)
+#define TSENS_8064_SENSOR9_EN				BIT(12)
+#define TSENS_8064_SENSOR10_EN				BIT(13)
+#define TSENS_8064_SENSORS_EN			(SENSORS_EN | \
+						TSENS_8064_SENSOR5_EN | \
+						TSENS_8064_SENSOR6_EN | \
+						TSENS_8064_SENSOR7_EN | \
+						TSENS_8064_SENSOR8_EN | \
+						TSENS_8064_SENSOR9_EN | \
+						TSENS_8064_SENSOR10_EN)
+#define TSENS_8064_STATUS_CNTL			(MSM_CLK_CTL_BASE + 0x00003660)
+#define TSENS_8064_S5_STATUS_ADDR		(MSM_CLK_CTL_BASE + 0x00003664)
+#define TSENS_8064_SEQ_SENSORS				5
+#define TSENS_8064_S4_S5_OFFSET				40
+
+static int tsens_status_cntl_start;
+
 struct tsens_tm_device_sensor {
 	struct thermal_zone_device	*tz_dev;
 	enum thermal_device_mode	mode;
@@ -121,11 +145,11 @@
 	int				offset;
 	int				calib_data;
 	int				calib_data_backup;
+	uint32_t			slope_mul_tsens_factor;
 };
 
 struct tsens_tm_device {
 	bool				prev_reading_avail;
-	int				slope_mul_tsens_factor;
 	int				tsens_factor;
 	uint32_t			tsens_num_sensor;
 	enum platform_type		hw_type;
@@ -137,26 +161,18 @@
 /* Temperature on y axis and ADC-code on x-axis */
 static int tsens_tz_code_to_degC(int adc_code, int sensor_num)
 {
-	int degC, degcbeforefactor;
-	degcbeforefactor = adc_code * tmdev->slope_mul_tsens_factor
-				+ tmdev->sensor[sensor_num].offset;
-	if (degcbeforefactor == 0)
-		degC = degcbeforefactor;
-	else if (degcbeforefactor > 0)
-		degC = (degcbeforefactor + tmdev->tsens_factor/2)
-						/ tmdev->tsens_factor;
-	else  /* rounding for negative degrees */
-		degC = (degcbeforefactor - tmdev->tsens_factor/2)
-						/ tmdev->tsens_factor;
-	return degC;
+	int degc;
+	degc = (adc_code * tmdev->sensor[sensor_num].slope_mul_tsens_factor
+			+ tmdev->sensor[sensor_num].offset)
+			/ tmdev->tsens_factor;
+	return degc;
 }
 
 static int tsens_tz_degC_to_code(int degC, int sensor_num)
 {
 	int code = (degC * tmdev->tsens_factor -
-			tmdev->sensor[sensor_num].offset
-			+ tmdev->slope_mul_tsens_factor/2)
-			/ tmdev->slope_mul_tsens_factor;
+			tmdev->sensor[sensor_num].offset)
+			/ tmdev->sensor[sensor_num].slope_mul_tsens_factor;
 
 	if (code > TSENS_THRESHOLD_MAX_CODE)
 		code = TSENS_THRESHOLD_MAX_CODE;
@@ -167,7 +183,7 @@
 
 static void tsens8960_get_temp(int sensor_num, unsigned long *temp)
 {
-	unsigned int code;
+	unsigned int code, offset = 0, sensor_addr;
 
 	if (!tmdev->prev_reading_avail) {
 		while (!(readl_relaxed(TSENS_INT_STATUS_ADDR)
@@ -176,7 +192,12 @@
 				TSENS_TRDY_RDY_MAX_TIME);
 		tmdev->prev_reading_avail = true;
 	}
-	code = readl_relaxed(TSENS_S0_STATUS_ADDR +
+
+	sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR;
+	if (tmdev->hw_type == APQ_8064 &&
+			sensor_num >= TSENS_8064_SEQ_SENSORS)
+		offset = TSENS_8064_S4_S5_OFFSET;
+	code = readl_relaxed(sensor_addr + offset +
 			(sensor_num << TSENS_STATUS_ADDR_OFFSET));
 	*temp = tsens_tz_code_to_degC(code, sensor_num);
 }
@@ -247,7 +268,8 @@
 			}
 			writel_relaxed(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
 			if (tmdev->hw_type == MSM_8960 ||
-						tmdev->hw_type == MSM_9615)
+				tmdev->hw_type == MSM_9615 ||
+				tmdev->hw_type == APQ_8064)
 				reg |= mask | TSENS_8960_SLP_CLK_ENA
 							| TSENS_EN;
 			else
@@ -257,7 +279,11 @@
 		} else {
 			reg &= ~mask;
 			if (!(reg & SENSOR0_EN)) {
-				if (tmdev->hw_type == MSM_8960 ||
+				if (tmdev->hw_type == APQ_8064)
+					reg &= ~(TSENS_8064_SENSORS_EN |
+						TSENS_8960_SLP_CLK_ENA |
+						TSENS_EN);
+				else if (tmdev->hw_type == MSM_8960 ||
 						tmdev->hw_type == MSM_9615)
 					reg &= ~(SENSORS_EN |
 						TSENS_8960_SLP_CLK_ENA |
@@ -319,7 +345,11 @@
 	lo_code = TSENS_THRESHOLD_MIN_CODE;
 	hi_code = TSENS_THRESHOLD_MAX_CODE;
 
-	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == APQ_8064)
+		reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+	else
+		reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+
 	reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR);
 	switch (trip) {
 	case TSENS_TRIP_STAGE3:
@@ -386,14 +416,21 @@
 		return -EINVAL;
 	}
 
-	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
-		writel_relaxed(reg_cntl | mask, TSENS_CNTL_ADDR);
-	else {
+	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) {
+		if (tmdev->hw_type == APQ_8064)
+			writel_relaxed(reg_cntl | mask, TSENS_8064_STATUS_CNTL);
+		else
+			writel_relaxed(reg_cntl | mask, TSENS_CNTL_ADDR);
+	} else {
 		if (code < lo_code || code > hi_code) {
 			pr_info("%s with invalid code %x\n", __func__, code);
 			return -EINVAL;
 		}
-		writel_relaxed(reg_cntl & ~mask, TSENS_CNTL_ADDR);
+		if (tmdev->hw_type == APQ_8064)
+			writel_relaxed(reg_cntl & ~mask,
+					TSENS_8064_STATUS_CNTL);
+		else
+			writel_relaxed(reg_cntl & ~mask, TSENS_CNTL_ADDR);
 	}
 	mb();
 	return 0;
@@ -465,7 +502,10 @@
 	lo_code = TSENS_THRESHOLD_MIN_CODE;
 	hi_code = TSENS_THRESHOLD_MAX_CODE;
 
-	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == APQ_8064)
+		reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+	else
+		reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
 	reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR);
 	switch (trip) {
 	case TSENS_TRIP_STAGE3:
@@ -561,25 +601,40 @@
 {
 	struct tsens_tm_device *tm = data;
 	unsigned int threshold, threshold_low, i, code, reg, sensor, mask;
+	unsigned int sensor_addr;
 	bool upper_th_x, lower_th_x;
 	int adc_code;
 
-	reg = readl_relaxed(TSENS_CNTL_ADDR);
-	writel_relaxed(reg | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR,
-			TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == APQ_8064) {
+		reg = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		writel_relaxed(reg | TSENS_LOWER_STATUS_CLR |
+			TSENS_UPPER_STATUS_CLR, TSENS_8064_STATUS_CNTL);
+	} else {
+		reg = readl_relaxed(TSENS_CNTL_ADDR);
+		writel_relaxed(reg | TSENS_LOWER_STATUS_CLR |
+			TSENS_UPPER_STATUS_CLR, TSENS_CNTL_ADDR);
+	}
 	mask = ~(TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR);
 	threshold = readl_relaxed(TSENS_THRESHOLD_ADDR);
 	threshold_low = (threshold & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
 					>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
 	threshold = (threshold & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
 					>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
-	reg = sensor = readl_relaxed(TSENS_CNTL_ADDR);
-	sensor &= (uint32_t) SENSORS_EN;
+	sensor = readl_relaxed(TSENS_CNTL_ADDR);
+	if (tmdev->hw_type == APQ_8064) {
+		reg = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		sensor &= (uint32_t) TSENS_8064_SENSORS_EN;
+	} else {
+		reg = sensor;
+		sensor &= (uint32_t) SENSORS_EN;
+	}
 	sensor >>= TSENS_SENSOR0_SHIFT;
+	sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR;
 	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+		if (i == TSENS_8064_SEQ_SENSORS)
+			sensor_addr += 40;
 		if (sensor & TSENS_MASK1) {
-			code = readl_relaxed(TSENS_S0_STATUS_ADDR +
-					(i << TSENS_STATUS_ADDR_OFFSET));
+			code = readl_relaxed(sensor_addr);
 			upper_th_x = code >= threshold;
 			lower_th_x = code <= threshold_low;
 			if (upper_th_x)
@@ -589,8 +644,7 @@
 			if (upper_th_x || lower_th_x) {
 				/* Notify user space */
 				schedule_work(&tm->sensor[i].work);
-				adc_code = readl_relaxed(TSENS_S0_STATUS_ADDR
-					+ (i << TSENS_STATUS_ADDR_OFFSET));
+				adc_code = readl_relaxed(sensor_addr);
 				pr_info("\nTrip point triggered by "
 					"current temperature (%d degrees) "
 					"measured by Temperature-Sensor %d\n",
@@ -598,7 +652,11 @@
 			}
 		}
 		sensor >>= 1;
+		sensor_addr += TSENS_SENSOR_STATUS_SIZE;
 	}
+	if (tmdev->hw_type == APQ_8064)
+		writel_relaxed(reg & mask, TSENS_8064_STATUS_CNTL);
+	else
 	writel_relaxed(reg & mask, TSENS_CNTL_ADDR);
 	mb();
 	return IRQ_HANDLED;
@@ -609,7 +667,8 @@
 	unsigned int reg_cntl = 0;
 
 	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
-	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MSM_9615)
+	if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MSM_9615 ||
+			tmdev->hw_type == APQ_8064)
 		writel_relaxed(reg_cntl &
 				~((((1 << tmdev->tsens_num_sensor) - 1) <<
 				TSENS_SENSOR0_SHIFT) | TSENS_8960_SLP_CLK_ENA
@@ -624,6 +683,7 @@
 static void tsens_hw_init(void)
 {
 	unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
+	unsigned int reg_status_cntl = 0;
 
 	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
 	writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR);
@@ -657,6 +717,25 @@
 				(TSENS_8660_CONFIG << TSENS_8660_CONFIG_SHIFT);
 
 		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+	} else if (tmdev->hw_type == APQ_8064) {
+		reg_cntl |= TSENS_8960_SLP_CLK_ENA |
+			(TSENS_MEASURE_PERIOD << 18) |
+			(((1 << tmdev->tsens_num_sensor) - 1) <<
+			 TSENS_SENSOR0_SHIFT);
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+		reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
+		reg_status_cntl |= TSENS_LOWER_STATUS_CLR |
+			TSENS_UPPER_STATUS_CLR |
+			TSENS_MIN_STATUS_MASK |
+			TSENS_MAX_STATUS_MASK;
+		writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL);
+		reg_cntl |= TSENS_EN;
+		writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
+
+		reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
+		reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
+			(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
+		writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
 	}
 
 	reg_thr |= (TSENS_LOWER_LIMIT_TH << TSENS_THRESHOLD_LOWER_LIMIT_SHIFT) |
@@ -692,8 +771,10 @@
 	}
 
 	tmdev->sensor[TSENS_MAIN_SENSOR].offset = tmdev->tsens_factor *
-		TSENS_CAL_DEGC - tmdev->slope_mul_tsens_factor *
+		TSENS_CAL_DEGC -
+		tmdev->sensor[TSENS_MAIN_SENSOR].slope_mul_tsens_factor *
 		tmdev->sensor[TSENS_MAIN_SENSOR].calib_data;
+
 	tmdev->prev_reading_avail = false;
 	INIT_WORK(&tmdev->sensor[TSENS_MAIN_SENSOR].work,
 						notify_uspace_tsens_fn);
@@ -703,35 +784,27 @@
 
 static int tsens_calib_sensors8960(void)
 {
-	uint32_t *main_sensor_addr, sensor_shift, *backup_sensor_addr;
-	uint32_t sensor_mask, i;
+	uint32_t i;
+	uint8_t *main_sensor_addr, *backup_sensor_addr;
 	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
-		main_sensor_addr = TSENS_8960_QFPROM_ADDR0 +
-			(TSENS_8960_QFPROM_SHIFT *
-		((i & TSENS_8960_QFPROM_SHIFT) >> TSENS_SENSOR_QFPROM_SHIFT));
-		sensor_shift = (i % TSENS_8960_QFPROM_SHIFT) * TSENS_RED_SHIFT;
-		sensor_mask = TSENS_THRESHOLD_MAX_CODE << sensor_shift;
-		backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 +
-			(TSENS_8960_QFPROM_SHIFT *
-		((i & TSENS_8960_QFPROM_SHIFT) >> TSENS_SENSOR_QFPROM_SHIFT));
+		main_sensor_addr = TSENS_8960_QFPROM_ADDR0 + i;
+		backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 + i;
 
-		tmdev->sensor[i].calib_data = (readl_relaxed(main_sensor_addr)
-			& sensor_mask) >> sensor_shift;
+		tmdev->sensor[i].calib_data = readb_relaxed(main_sensor_addr);
 		tmdev->sensor[i].calib_data_backup =
-			(readl_relaxed(backup_sensor_addr) &
-				sensor_mask) >> sensor_shift;
+			readb_relaxed(backup_sensor_addr);
 		if (tmdev->sensor[i].calib_data_backup)
 			tmdev->sensor[i].calib_data =
 				tmdev->sensor[i].calib_data_backup;
-
 		if (!tmdev->sensor[i].calib_data) {
 			WARN(1, "%s: No temperature sensor:%d data for"
 			" calibration in QFPROM!\n", __func__, i);
 			return -ENODEV;
 		}
-		tmdev->sensor[i].offset = tmdev->tsens_factor *
-			TSENS_CAL_DEGC - tmdev->slope_mul_tsens_factor *
-			tmdev->sensor[i].calib_data;
+		tmdev->sensor[i].offset = (TSENS_CAL_DEGC *
+			tmdev->tsens_factor)
+			- (tmdev->sensor[i].calib_data *
+			tmdev->sensor[i].slope_mul_tsens_factor);
 		tmdev->prev_reading_avail = false;
 		INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn);
 	}
@@ -756,7 +829,8 @@
 
 	if (tmdev->hw_type == MSM_8660)
 		rc = tsens_calib_sensors8660();
-	else if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MSM_9615)
+	else if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MSM_9615 ||
+		tmdev->hw_type == APQ_8064)
 		rc = tsens_calib_sensors8960();
 
 	return rc;
@@ -764,7 +838,7 @@
 
 int msm_tsens_early_init(struct tsens_platform_data *pdata)
 {
-	int rc = 0;
+	int rc = 0, i;
 
 	if (!pdata) {
 		pr_err("No TSENS Platform data\n");
@@ -780,7 +854,8 @@
 		return -ENOMEM;
 	}
 
-	tmdev->slope_mul_tsens_factor = pdata->slope;
+	for (i = 0; i < pdata->tsens_num_sensor; i++)
+		tmdev->sensor[i].slope_mul_tsens_factor = pdata->slope[i];
 	tmdev->tsens_factor = pdata->tsens_factor;
 	tmdev->tsens_num_sensor = pdata->tsens_num_sensor;
 	tmdev->hw_type = pdata->hw_type;
@@ -799,9 +874,14 @@
 		return rc;
 	}
 
+	if (tmdev->hw_type == APQ_8064)
+		tsens_status_cntl_start = 0;
+	else
+		tsens_status_cntl_start = TSENS_STATUS_CNTL_OFFSET;
+
 	tsens_hw_init();
 
-	pr_info("msm_tsens_early_init: done\n");
+	pr_debug("msm_tsens_early_init: done\n");
 
 	return rc;
 }
@@ -809,6 +889,7 @@
 static int __init tsens_tm_init(void)
 {
 	int rc, i;
+	unsigned int reg_cntl, reg_status;
 
 	if (!tmdev) {
 		pr_info("%s : TSENS early init not done.\n", __func__);
@@ -823,7 +904,7 @@
 		tmdev->sensor[i].tz_dev = thermal_zone_device_register(name,
 				TSENS_TRIP_NUM, &tmdev->sensor[i],
 				&tsens_thermal_zone_ops, 0, 0, 0, 0);
-		if (tmdev->sensor[i].tz_dev == NULL) {
+		if (IS_ERR(tmdev->sensor[i].tz_dev)) {
 			pr_err("%s: thermal_zone_device_register() failed.\n",
 			__func__);
 			rc = -ENODEV;
@@ -841,8 +922,11 @@
 		goto fail;
 	}
 
-	pr_notice("%s: OK\n", __func__);
+	reg_status = readl_relaxed(TSENS_8064_STATUS_CNTL);
+	pr_debug("%s: OK\n", __func__);
 	mb();
+	reg_status = readl_relaxed(TSENS_8064_STATUS_CNTL);
+	reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
 	return 0;
 fail:
 	tsens_disable_mode();