[source] ipq806x: add ipq806x specific tsens driver

LEDE Commits lede-commits at lists.infradead.org
Sun Mar 26 23:34:39 PDT 2017


blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/dc32d0a53cc01d9f644c6e9851b6770440d4b28c

commit dc32d0a53cc01d9f644c6e9851b6770440d4b28c
Author: Pavel Kubelun <be.dissent at gmail.com>
AuthorDate: Thu Mar 23 17:59:30 2017 +0300

    ipq806x: add ipq806x specific tsens driver
    
    Current upstream driver doesnt fully support ipq806x devices
    ipq806x has 11 sensors, the upstream one doesn't allow to check
    sensors 0-4, only 5-10.
    
    A specific driver for ipq806x has been found in Qualcomm SDK repo.
    
    https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-msm/commit/?h=release/endive_preview_cc&id=c089e464cd7ce652419a0dc44d7959ce4d24b8a5
    https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-msm/commit/?h=release/endive_preview_cc&id=c23d94b702c4182862e7f5051a2b7d00bb922a29
    https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-msm/commit/?h=release/endive_preview_cc&id=742f3684b62a6b9f082cb49404b1a92dc0b16bf5
    https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-msm/commit/?h=release/endive_preview_cc&id=c0a9b2e2a382c152fa128f5b864c800dd6dfb311
    
    Merging it into LEDE with this commit.
    
    Signed-off-by: Pavel Kubelun <be.dissent at gmail.com>
---
 .../files-4.9/arch/arm/boot/dts/qcom-ipq8064.dtsi  | 391 +++++++++++--
 .../0030-clk-Disable-i2c-device-on-gsbi4.patch     |   2 +-
 .../patches-4.9/0063-1-ipq806x-tsens-driver.patch  | 638 +++++++++++++++++++++
 ...3-2-tsens-support-configurable-interrupts.patch | 474 +++++++++++++++
 ...0063-ipq806x-clk-gcc-add-tsens-child-node.patch |  46 --
 5 files changed, 1445 insertions(+), 106 deletions(-)

diff --git a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064.dtsi b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064.dtsi
index 3893f45..dd55754 100644
--- a/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/target/linux/ipq806x/files-4.9/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -78,86 +78,354 @@
 	};
 
 	thermal-zones {
-		cpu-thermal0 {
-			polling-delay-passive = <250>;
-			polling-delay = <1000>;
+		tsens_tz_sensor0 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 0>;
 
-			thermal-sensors = <&gcc 5>;
-			coefficients = <1132 0>;
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
+
+		tsens_tz_sensor1 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 1>;
+
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
+
+		tsens_tz_sensor2 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 2>;
+
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
+
+		tsens_tz_sensor3 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 3>;
 
 			trips {
-				cpu_alert0: trip0 {
-					temperature = <75000>;
-					hysteresis = <2000>;
-					type = "passive";
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
 				};
-				cpu_crit0: trip1 {
-					temperature = <110000>;
-					hysteresis = <2000>;
-					type = "critical";
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
 				};
 			};
 		};
 
-		cpu-thermal1 {
-			polling-delay-passive = <250>;
-			polling-delay = <1000>;
+		tsens_tz_sensor4 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 4>;
+
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
+
+		tsens_tz_sensor5 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 5>;
+
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
 
-			thermal-sensors = <&gcc 6>;
-			coefficients = <1132 0>;
+		tsens_tz_sensor6 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 6>;
 
 			trips {
-				cpu_alert1: trip0 {
-					temperature = <75000>;
-					hysteresis = <2000>;
-					type = "passive";
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
 				};
-				cpu_crit1: trip1 {
-					temperature = <110000>;
-					hysteresis = <2000>;
-					type = "critical";
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
 				};
 			};
 		};
 
-		cpu-thermal2 {
-			polling-delay-passive = <250>;
-			polling-delay = <1000>;
+		tsens_tz_sensor7 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 7>;
 
-			thermal-sensors = <&gcc 7>;
-			coefficients = <1199 0>;
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
+
+		tsens_tz_sensor8 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 8>;
 
 			trips {
-				cpu_alert2: trip0 {
-					temperature = <75000>;
-					hysteresis = <2000>;
-					type = "passive";
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
 				};
-				cpu_crit2: trip1 {
-					temperature = <110000>;
-					hysteresis = <2000>;
-					type = "critical";
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
 				};
 			};
 		};
 
-		cpu-thermal3 {
-			polling-delay-passive = <250>;
-			polling-delay = <1000>;
+		tsens_tz_sensor9 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 9>;
+
+			trips {
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
+				};
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
+				};
+			};
+		};
 
-			thermal-sensors = <&gcc 8>;
-			coefficients = <1132 0>;
+		tsens_tz_sensor10 {
+			polling-delay-passive = <0>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens 10>;
 
 			trips {
-				cpu_alert3: trip0 {
-					temperature = <75000>;
-					hysteresis = <2000>;
-					type = "passive";
+				cpu-critical-hi {
+					temperature = <125>;
+					hysteresis = <2>;
+					type = "critical_high";
 				};
-				cpu_crit3: trip1 {
-					temperature = <110000>;
-					hysteresis = <2000>;
-					type = "critical";
+
+				cpu-config-hi {
+					temperature = <105>;
+					hysteresis = <2>;
+					type = "configurable_hi";
+				};
+
+				cpu-config-lo {
+					temperature = <95>;
+					hysteresis = <2>;
+					type = "configurable_lo";
+				};
+
+				cpu-critical-low {
+					temperature = <0>;
+					hysteresis = <2>;
+					type = "critical_low";
 				};
 			};
 		};
@@ -273,15 +541,14 @@
 
 		qfprom: qfprom at 700000 {
 			compatible = "qcom,qfprom", "syscon";
-			reg = <0x00700000 0x1000>;
+			reg = <0x700000 0x1000>;
 			#address-cells = <1>;
 			#size-cells = <1>;
-			ranges;
-
-			tsens_calib: calib {
+			status = "okay";
+			tsens_calib: calib at 400 {
 				reg = <0x400 0x10>;
 			};
-			tsens_backup: backup_calib {
+			tsens_backup: backup at 410 {
 				reg = <0x410 0x10>;
 			};
 		};
@@ -620,11 +887,17 @@
 		gcc: clock-controller at 900000 {
 			compatible = "qcom,gcc-ipq8064";
 			reg = <0x00900000 0x4000>;
-			nvmem-cells = <&tsens_calib>, <&tsens_backup>;
-			nvmem-cell-names = "calib", "calib_backup";
 			#clock-cells = <1>;
 			#reset-cells = <1>;
 			#power-domain-cells = <1>;
+		};
+
+		tsens: thermal-sensor at 900000 {
+			compatible = "qcom,ipq8064-tsens";
+			reg = <0x900000 0x3680>;
+			nvmem-cells = <&tsens_calib>, <&tsens_backup>;
+			nvmem-cell-names = "calib", "calib_backup";
+			interrupts = <0 178 0>;
 			#thermal-sensor-cells = <1>;
 		};
 
diff --git a/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch
index a4fe548..b2a6afe 100644
--- a/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch
+++ b/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch
@@ -34,7 +34,7 @@ Signed-off-by: John Crispin <john at phrozen.org>
  		.hw.init = &(struct clk_init_data){
  			.name = "gsbi1_h_clk",
  			.ops = &clk_branch_ops,
-++			.flags = CLK_IGNORE_UNUSED,
++			.flags = CLK_IGNORE_UNUSED,
  		},
  	},
  };
diff --git a/target/linux/ipq806x/patches-4.9/0063-1-ipq806x-tsens-driver.patch b/target/linux/ipq806x/patches-4.9/0063-1-ipq806x-tsens-driver.patch
new file mode 100644
index 0000000..d8205c1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0063-1-ipq806x-tsens-driver.patch
@@ -0,0 +1,638 @@
+From 3302e1e1a3cfa4e67fda2a61d6f0c42205d40932 Mon Sep 17 00:00:00 2001
+From: Rajith Cherian <rajith at codeaurora.org>
+Date: Tue, 14 Feb 2017 18:30:43 +0530
+Subject: [PATCH] ipq8064: tsens: Base tsens driver for IPQ8064
+
+Add TSENS driver template to support IPQ8064.
+This is a base file copied from tsens-8960.c
+
+Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7
+Signed-off-by: Rajith Cherian <rajith at codeaurora.org>
+
+ipq8064: tsens: TSENS driver support for IPQ8064
+
+Support for IPQ8064 tsens driver. The driver works
+with the thermal framework. The driver overrides the
+following fucntionalities:
+
+1. Get current temperature.
+2. Get/Set trip temperatures.
+3. Enabled/Disable trip points.
+4. ISR for threshold generated interrupt.
+5. Notify userspace when trip points are hit.
+
+Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918
+Signed-off-by: Rajith Cherian <rajith at codeaurora.org>
+---
+ .../devicetree/bindings/thermal/qcom-tsens.txt     |   1 +
+ drivers/thermal/qcom/Makefile                      |   3 +-
+ drivers/thermal/qcom/tsens-ipq8064.c               | 551 +++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c                       |   3 +
+ drivers/thermal/qcom/tsens.h                       |   2 +-
+ 5 files changed, 558 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/thermal/qcom/tsens-ipq8064.c
+
+diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+index 292ed89..f4a76f6 100644
+--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+@@ -5,6 +5,7 @@ Required properties:
+  - "qcom,msm8916-tsens" : For 8916 Family of SoCs
+  - "qcom,msm8974-tsens" : For 8974 Family of SoCs
+  - "qcom,msm8996-tsens" : For 8996 Family of SoCs
++ - "qcom,ipq8064-tsens" : For IPQ8064
+ 
+ - reg: Address range of the thermal registers
+ - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
+diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
+index 2cc2193..cc07cf4 100644
+--- a/drivers/thermal/qcom/Makefile
++++ b/drivers/thermal/qcom/Makefile
+@@ -1,2 +1,3 @@
+ obj-$(CONFIG_QCOM_TSENS)	+= qcom_tsens.o
+-qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o
++qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o \
++				tsens-ipq8064.o
+diff --git a/drivers/thermal/qcom/tsens-ipq8064.c b/drivers/thermal/qcom/tsens-ipq8064.c
+new file mode 100644
+index 0000000..c52888f
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-ipq8064.c
+@@ -0,0 +1,551 @@
++/*
++ * Copyright (c) 2015, 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
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/regmap.h>
++#include <linux/thermal.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include "tsens.h"
++
++#define CAL_MDEGC		30000
++
++#define CONFIG_ADDR		0x3640
++/* CONFIG_ADDR bitmasks */
++#define CONFIG			0x9b
++#define CONFIG_MASK		0xf
++#define CONFIG_SHIFT		0
++
++#define STATUS_CNTL_8064	0x3660
++#define CNTL_ADDR		0x3620
++/* CNTL_ADDR bitmasks */
++#define EN			BIT(0)
++#define SW_RST			BIT(1)
++#define SENSOR0_EN		BIT(3)
++#define SLP_CLK_ENA		BIT(26)
++#define MEASURE_PERIOD		1
++#define SENSOR0_SHIFT		3
++
++/* INT_STATUS_ADDR bitmasks */
++#define MIN_STATUS_MASK		BIT(0)
++#define LOWER_STATUS_CLR	BIT(1)
++#define UPPER_STATUS_CLR	BIT(2)
++#define MAX_STATUS_MASK		BIT(3)
++
++#define THRESHOLD_ADDR		0x3624
++/* THRESHOLD_ADDR bitmasks */
++#define THRESHOLD_MAX_CODE		0xff
++#define THRESHOLD_MIN_CODE		0
++#define THRESHOLD_MAX_LIMIT_SHIFT	24
++#define THRESHOLD_MIN_LIMIT_SHIFT	16
++#define THRESHOLD_UPPER_LIMIT_SHIFT	8
++#define THRESHOLD_LOWER_LIMIT_SHIFT	0
++#define THRESHOLD_MAX_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
++						THRESHOLD_MAX_LIMIT_SHIFT)
++#define THRESHOLD_MIN_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
++						THRESHOLD_MIN_LIMIT_SHIFT)
++#define THRESHOLD_UPPER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
++						THRESHOLD_UPPER_LIMIT_SHIFT)
++#define THRESHOLD_LOWER_LIMIT_MASK	(THRESHOLD_MAX_CODE << \
++						THRESHOLD_LOWER_LIMIT_SHIFT)
++
++/* Initial temperature threshold values */
++#define LOWER_LIMIT_TH		0x9d /* 95C */
++#define UPPER_LIMIT_TH		0xa6 /* 105C */
++#define MIN_LIMIT_TH		0x0
++#define MAX_LIMIT_TH		0xff
++
++#define S0_STATUS_ADDR		0x3628
++#define STATUS_ADDR_OFFSET	2
++#define SENSOR_STATUS_SIZE	4
++#define INT_STATUS_ADDR		0x363c
++#define TRDY_MASK		BIT(7)
++#define TIMEOUT_US		100
++
++#define TSENS_EN		BIT(0)
++#define TSENS_SW_RST		BIT(1)
++#define TSENS_ADC_CLK_SEL	BIT(2)
++#define SENSOR0_EN		BIT(3)
++#define SENSOR1_EN		BIT(4)
++#define SENSOR2_EN		BIT(5)
++#define SENSOR3_EN		BIT(6)
++#define SENSOR4_EN		BIT(7)
++#define SENSORS_EN		(SENSOR0_EN | SENSOR1_EN | \
++				SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
++#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_SEQ_SENSORS	5
++#define TSENS_8064_S4_S5_OFFSET	40
++#define TSENS_FACTOR		1000
++
++/* Trips: from very hot to very cold */
++enum tsens_trip_type {
++	TSENS_TRIP_STAGE3 = 0,
++	TSENS_TRIP_STAGE2,
++	TSENS_TRIP_STAGE1,
++	TSENS_TRIP_STAGE0,
++	TSENS_TRIP_NUM,
++};
++
++u32 tsens_8064_slope[] = {
++			1176, 1176, 1154, 1176,
++			1111, 1132, 1132, 1199,
++			1132, 1199, 1132
++			};
++
++/* Temperature on y axis and ADC-code on x-axis */
++static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s)
++{
++	int degcbeforefactor, degc;
++
++	degcbeforefactor = (adc_code * s->slope) + s->offset;
++
++	if (degcbeforefactor == 0)
++		degc = degcbeforefactor;
++	else if (degcbeforefactor > 0)
++		degc = (degcbeforefactor + TSENS_FACTOR/2)
++			/ TSENS_FACTOR;
++	else
++		degc = (degcbeforefactor - TSENS_FACTOR/2)
++			/ TSENS_FACTOR;
++
++	return degc;
++}
++
++static int degC_to_code(int degC, const struct tsens_sensor *s)
++{
++	int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2))
++			/ s->slope;
++
++	if (code > THRESHOLD_MAX_CODE)
++		code = THRESHOLD_MAX_CODE;
++	else if (code < THRESHOLD_MIN_CODE)
++		code = THRESHOLD_MIN_CODE;
++	return code;
++}
++
++static int suspend_ipq8064(struct tsens_device *tmdev)
++{
++	int ret;
++	unsigned int mask;
++	struct regmap *map = tmdev->map;
++
++	ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
++	if (ret)
++		return ret;
++
++	ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
++	if (ret)
++		return ret;
++
++	mask = SLP_CLK_ENA | EN;
++
++	ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int resume_ipq8064(struct tsens_device *tmdev)
++{
++	int ret;
++	struct regmap *map = tmdev->map;
++
++	ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
++	if (ret)
++		return ret;
++
++	ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
++	if (ret)
++		return ret;
++
++	ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
++	if (ret)
++		return ret;
++
++	ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static void notify_uspace_tsens_fn(struct work_struct *work)
++{
++	struct tsens_sensor *s = container_of(work, struct tsens_sensor,
++								notify_work);
++
++	sysfs_notify(&s->tzd->device.kobj, NULL, "type");
++}
++
++static void tsens_scheduler_fn(struct work_struct *work)
++{
++	struct tsens_device *tmdev = container_of(work, struct tsens_device,
++					tsens_work);
++	unsigned int threshold, threshold_low, code, reg, sensor, mask;
++	unsigned int sensor_addr;
++	bool upper_th_x, lower_th_x;
++	int adc_code, ret;
++
++	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg);
++	if (ret)
++		return;
++	reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
++	ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg);
++	if (ret)
++		return;
++
++	mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
++	ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold);
++	if (ret)
++		return;
++	threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK)
++				>> THRESHOLD_LOWER_LIMIT_SHIFT;
++	threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK)
++				>> THRESHOLD_UPPER_LIMIT_SHIFT;
++
++	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg);
++	if (ret)
++		return;
++
++	ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor);
++	if (ret)
++		return;
++	sensor &= (uint32_t) TSENS_8064_SENSORS_EN;
++	sensor >>= SENSOR0_SHIFT;
++
++	/* Constraint: There is only 1 interrupt control register for all
++	 * 11 temperature sensor. So monitoring more than 1 sensor based
++	 * on interrupts will yield inconsistent result. To overcome this
++	 * issue we will monitor only sensor 0 which is the master sensor.
++	 */
++
++	/* Skip if the sensor is disabled */
++	if (sensor & 1) {
++		ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code);
++		if (ret)
++			return;
++		upper_th_x = code >= threshold;
++		lower_th_x = code <= threshold_low;
++		if (upper_th_x)
++			mask |= UPPER_STATUS_CLR;
++		if (lower_th_x)
++			mask |= LOWER_STATUS_CLR;
++		if (upper_th_x || lower_th_x) {
++			/* Notify user space */
++			schedule_work(&tmdev->sensor[0].notify_work);
++			regmap_read(tmdev->map, sensor_addr, &adc_code);
++			pr_debug("Trigger (%d degrees) for sensor %d\n",
++				code_to_degC(adc_code, &tmdev->sensor[0]), 0);
++		}
++	}
++	regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask);
++
++	/* force memory to sync */
++	mb();
++}
++
++static irqreturn_t tsens_isr(int irq, void *data)
++{
++	struct tsens_device *tmdev = data;
++
++	schedule_work(&tmdev->tsens_work);
++	return IRQ_HANDLED;
++}
++
++static void hw_init(struct tsens_device *tmdev)
++{
++	int ret;
++	unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
++	unsigned int reg_status_cntl = 0;
++
++	regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
++	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
++
++	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18)
++		| (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT);
++	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++	regmap_read(tmdev->map, STATUS_CNTL_8064, &reg_status_cntl);
++	reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR
++			| MIN_STATUS_MASK | MAX_STATUS_MASK;
++	regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl);
++	reg_cntl |= TSENS_EN;
++	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++
++	regmap_read(tmdev->map, CONFIG_ADDR, &reg_cfg);
++	reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT);
++	regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg);
++
++	reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT)
++		| (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT)
++		| (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT)
++		| (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
++
++	regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr);
++
++	ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr,
++			IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev);
++	if (ret < 0) {
++		pr_err("%s: request_irq FAIL: %d\n", __func__, ret);
++		return;
++	}
++
++	INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn);
++}
++
++static int init_ipq8064(struct tsens_device *tmdev)
++{
++	int ret, i;
++	u32 reg_cntl, offset = 0;
++
++	init_common(tmdev);
++	if (!tmdev->map)
++		return -ENODEV;
++
++	/*
++	 * The status registers for each sensor are discontiguous
++	 * because some SoCs have 5 sensors while others have more
++	 * but the control registers stay in the same place, i.e
++	 * directly after the first 5 status registers.
++	 */
++	for (i = 0; i < tmdev->num_sensors; i++) {
++		if (i >= TSENS_8064_SEQ_SENSORS)
++			offset = TSENS_8064_S4_S5_OFFSET;
++
++		tmdev->sensor[i].status = S0_STATUS_ADDR + offset
++					+ (i << STATUS_ADDR_OFFSET);
++		tmdev->sensor[i].slope = tsens_8064_slope[i];
++		INIT_WORK(&tmdev->sensor[i].notify_work,
++						notify_uspace_tsens_fn);
++	}
++
++	reg_cntl = SW_RST;
++	ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
++	if (ret)
++		return ret;
++
++	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
++	reg_cntl &= ~SW_RST;
++	ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
++					 CONFIG_MASK, CONFIG);
++
++	reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
++	ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++	if (ret)
++		return ret;
++
++	reg_cntl |= EN;
++	ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int calibrate_ipq8064(struct tsens_device *tmdev)
++{
++	int i;
++	char *data, *data_backup;
++
++	ssize_t num_read = tmdev->num_sensors;
++	struct tsens_sensor *s = tmdev->sensor;
++
++	data = qfprom_read(tmdev->dev, "calib");
++	if (IS_ERR(data)) {
++		pr_err("Calibration not found.\n");
++		return PTR_ERR(data);
++	}
++
++	data_backup = qfprom_read(tmdev->dev, "calib_backup");
++	if (IS_ERR(data_backup)) {
++		pr_err("Backup calibration not found.\n");
++		return PTR_ERR(data_backup);
++	}
++
++	for (i = 0; i < num_read; i++) {
++		s[i].calib_data = readb_relaxed(data + i);
++		s[i].calib_data_backup = readb_relaxed(data_backup + i);
++
++		if (s[i].calib_data_backup)
++			s[i].calib_data = s[i].calib_data_backup;
++		if (!s[i].calib_data) {
++			pr_err("QFPROM TSENS calibration data not present\n");
++			return -ENODEV;
++		}
++		s[i].slope = tsens_8064_slope[i];
++		s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope);
++	}
++
++	hw_init(tmdev);
++
++	return 0;
++}
++
++static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp)
++{
++	int ret;
++	u32 code, trdy;
++	const struct tsens_sensor *s = &tmdev->sensor[id];
++	unsigned long timeout;
++
++	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
++	do {
++		ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
++		if (ret)
++			return ret;
++		if (!(trdy & TRDY_MASK))
++			continue;
++		ret = regmap_read(tmdev->map, s->status, &code);
++		if (ret)
++			return ret;
++		*temp = code_to_degC(code, s);
++		return 0;
++	} while (time_before(jiffies, timeout));
++
++	return -ETIMEDOUT;
++}
++
++static int set_trip_temp_ipq8064(void *data, int trip, int temp)
++{
++	unsigned int reg_th, reg_cntl;
++	int ret, code, code_chk, hi_code, lo_code;
++	const struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
++
++	code_chk = code = degC_to_code(temp, s);
++
++	if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE)
++		return -EINVAL;
++
++	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg_cntl);
++	if (ret)
++		return ret;
++
++	ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &reg_th);
++	if (ret)
++		return ret;
++
++	hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK)
++			>> THRESHOLD_UPPER_LIMIT_SHIFT;
++	lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK)
++			>> THRESHOLD_LOWER_LIMIT_SHIFT;
++
++	switch (trip) {
++	case TSENS_TRIP_STAGE3:
++		code <<= THRESHOLD_MAX_LIMIT_SHIFT;
++		reg_th &= ~THRESHOLD_MAX_LIMIT_MASK;
++		break;
++	case TSENS_TRIP_STAGE2:
++		if (code_chk <= lo_code)
++			return -EINVAL;
++		code <<= THRESHOLD_UPPER_LIMIT_SHIFT;
++		reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK;
++		break;
++	case TSENS_TRIP_STAGE1:
++		if (code_chk >= hi_code)
++			return -EINVAL;
++		code <<= THRESHOLD_LOWER_LIMIT_SHIFT;
++		reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK;
++		break;
++	case TSENS_TRIP_STAGE0:
++		code <<= THRESHOLD_MIN_LIMIT_SHIFT;
++		reg_th &= ~THRESHOLD_MIN_LIMIT_MASK;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int set_trip_activate_ipq8064(void *data, int trip,
++					enum thermal_trip_activation_mode mode)
++{
++	unsigned int reg_cntl, mask, val;
++	const struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
++	int ret;
++
++	if (!tmdev || trip < 0)
++		return -EINVAL;
++
++	ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg_cntl);
++	if (ret)
++		return ret;
++
++	switch (trip) {
++	case TSENS_TRIP_STAGE3:
++		mask = MAX_STATUS_MASK;
++		break;
++	case TSENS_TRIP_STAGE2:
++		mask = UPPER_STATUS_CLR;
++		break;
++	case TSENS_TRIP_STAGE1:
++		mask = LOWER_STATUS_CLR;
++		break;
++	case TSENS_TRIP_STAGE0:
++		mask = MIN_STATUS_MASK;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
++		val = reg_cntl | mask;
++	else
++		val = reg_cntl & ~mask;
++
++	ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val);
++	if (ret)
++		return ret;
++
++	/* force memory to sync */
++	mb();
++	return 0;
++}
++
++const struct tsens_ops ops_ipq8064 = {
++	.init		= init_ipq8064,
++	.calibrate	= calibrate_ipq8064,
++	.get_temp	= get_temp_ipq8064,
++	.suspend	= suspend_ipq8064,
++	.resume		= resume_ipq8064,
++	.set_trip_temp	= set_trip_temp_ipq8064,
++	.set_trip_activate = set_trip_activate_ipq8064,
++};
++
++const struct tsens_data data_ipq8064 = {
++	.num_sensors	= 11,
++	.ops		= &ops_ipq8064,
++};
+diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
+index 3f9fe6a..2d25593 100644
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -72,6 +72,9 @@ static const struct of_device_id tsens_table[] = {
+ 	}, {
+ 		.compatible = "qcom,msm8996-tsens",
+ 		.data = &data_8996,
++	}, {
++		.compatible = "qcom,ipq8064-tsens",
++		.data = &data_ipq8064,
+ 	},
+ 	{}
+ };
+diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
+index 911c197..31279a2 100644
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -89,6 +89,6 @@ void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
+ int init_common(struct tsens_device *);
+ int get_temp_common(struct tsens_device *, int, int *);
+ 
+-extern const struct tsens_data data_8916, data_8974, data_8960, data_8996;
++extern const struct tsens_data data_8916, data_8974, data_8960, data_8996, data_ipq8064;
+ 
+ #endif /* __QCOM_TSENS_H__ */
diff --git a/target/linux/ipq806x/patches-4.9/0063-2-tsens-support-configurable-interrupts.patch b/target/linux/ipq806x/patches-4.9/0063-2-tsens-support-configurable-interrupts.patch
new file mode 100644
index 0000000..de9a8f8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0063-2-tsens-support-configurable-interrupts.patch
@@ -0,0 +1,474 @@
+From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001
+From: Rajith Cherian <rajith at codeaurora.org>
+Date: Wed, 1 Feb 2017 19:00:26 +0530
+Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts
+
+Provide support for adding configurable high and
+configurable low trip temperatures. An interrupts is
+also triggerred when these trip points are hit. The
+interrupts can be activated or deactivated from sysfs.
+This functionality is made available only if
+CONFIG_THERMAL_WRITABLE_TRIPS is defined.
+
+Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934
+Signed-off-by: Rajith Cherian <rajith at codeaurora.org>
+---
+ .../devicetree/bindings/thermal/qcom-tsens.txt     |  4 ++
+ drivers/thermal/of-thermal.c                       | 63 ++++++++++++++++++----
+ drivers/thermal/qcom/tsens.c                       | 43 ++++++++++++---
+ drivers/thermal/qcom/tsens.h                       | 11 ++++
+ drivers/thermal/thermal_core.c                     | 44 ++++++++++++++-
+ include/linux/thermal.h                            | 14 +++++
+ 6 files changed, 162 insertions(+), 17 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+index f4a76f6..7c0a6a7 100644
+--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+@@ -12,11 +12,15 @@ Required properties:
+ - Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
+ nvmem cells
+ 
++Optional properties:
++- interrupts: Interrupt which gets triggered when threshold is hit
++
+ Example:
+ tsens: thermal-sensor at 900000 {
+ 		compatible = "qcom,msm8916-tsens";
+ 		reg = <0x4a8000 0x2000>;
+ 		nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
+ 		nvmem-cell-names = "caldata", "calsel";
++		interrupts = <0 178 0>;
+ 		#thermal-sensor-cells = <1>;
+ 	};
+diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
+index d04ec3b..d83697e 100644
+--- a/drivers/thermal/of-thermal.c
++++ b/drivers/thermal/of-thermal.c
+@@ -95,7 +95,7 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (!data->ops->get_temp)
++	if (!data->ops->get_temp || (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EINVAL;
+ 
+ 	return data->ops->get_temp(data->sensor_data, temp);
+@@ -106,7 +106,8 @@ static int of_thermal_set_trips(struct thermal_zone_device *tz,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (!data->ops || !data->ops->set_trips)
++	if (!data->ops || !data->ops->set_trips
++			|| (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EINVAL;
+ 
+ 	return data->ops->set_trips(data->sensor_data, low, high);
+@@ -192,6 +193,9 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
++	if (data->mode == THERMAL_DEVICE_DISABLED)
++		return -EINVAL;
++
+ 	return data->ops->set_emul_temp(data->sensor_data, temp);
+ }
+ 
+@@ -200,7 +204,7 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (!data->ops->get_trend)
++	if (!data->ops->get_trend || (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EINVAL;
+ 
+ 	return data->ops->get_trend(data->sensor_data, trip, trend);
+@@ -286,7 +290,9 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz,
+ 	mutex_unlock(&tz->lock);
+ 
+ 	data->mode = mode;
+-	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
++
++	if (mode == THERMAL_DEVICE_ENABLED)
++		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+ 
+ 	return 0;
+ }
+@@ -296,7 +302,8 @@ static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (trip >= data->ntrips || trip < 0)
++	if (trip >= data->ntrips || trip < 0
++				|| (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EDOM;
+ 
+ 	*type = data->trips[trip].type;
+@@ -304,12 +311,39 @@ static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
+ 	return 0;
+ }
+ 
++static int of_thermal_activate_trip_type(struct thermal_zone_device *tz,
++			int trip, enum thermal_trip_activation_mode mode)
++{
++	struct __thermal_zone *data = tz->devdata;
++
++	if (trip >= data->ntrips || trip < 0
++				|| (data->mode == THERMAL_DEVICE_DISABLED))
++		return -EDOM;
++
++	/*
++	 * The configurable_hi and configurable_lo trip points can be
++	 * activated and deactivated.
++	 */
++
++	if (data->ops->set_trip_activate) {
++		int ret;
++
++		ret = data->ops->set_trip_activate(data->sensor_data,
++								trip, mode);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
+ static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
+ 				    int *temp)
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (trip >= data->ntrips || trip < 0)
++	if (trip >= data->ntrips || trip < 0
++				|| (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EDOM;
+ 
+ 	*temp = data->trips[trip].temperature;
+@@ -322,7 +356,8 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (trip >= data->ntrips || trip < 0)
++	if (trip >= data->ntrips || trip < 0
++				|| (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EDOM;
+ 
+ 	if (data->ops->set_trip_temp) {
+@@ -344,7 +379,8 @@ static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (trip >= data->ntrips || trip < 0)
++	if (trip >= data->ntrips || trip < 0
++				|| (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EDOM;
+ 
+ 	*hyst = data->trips[trip].hysteresis;
+@@ -357,7 +393,8 @@ static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
+ {
+ 	struct __thermal_zone *data = tz->devdata;
+ 
+-	if (trip >= data->ntrips || trip < 0)
++	if (trip >= data->ntrips || trip < 0
++				|| (data->mode == THERMAL_DEVICE_DISABLED))
+ 		return -EDOM;
+ 
+ 	/* thermal framework should take care of data->mask & (1 << trip) */
+@@ -432,6 +469,9 @@ thermal_zone_of_add_sensor(struct device_node *zone,
+ 	if (ops->set_emul_temp)
+ 		tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
+ 
++	if (ops->set_trip_activate)
++		tzd->ops->set_trip_activate = of_thermal_activate_trip_type;
++
+ 	mutex_unlock(&tzd->lock);
+ 
+ 	return tzd;
+@@ -726,7 +766,10 @@ static const char * const trip_types[] = {
+ 	[THERMAL_TRIP_ACTIVE]	= "active",
+ 	[THERMAL_TRIP_PASSIVE]	= "passive",
+ 	[THERMAL_TRIP_HOT]	= "hot",
+-	[THERMAL_TRIP_CRITICAL]	= "critical",
++	[THERMAL_TRIP_CRITICAL]	= "critical_high",
++	[THERMAL_TRIP_CONFIGURABLE_HI] = "configurable_hi",
++	[THERMAL_TRIP_CONFIGURABLE_LOW] = "configurable_lo",
++	[THERMAL_TRIP_CRITICAL_LOW] = "critical_low",
+ };
+ 
+ /**
+diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
+index 2d25593..ac68af3 100644
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -31,7 +31,7 @@ static int tsens_get_temp(void *data, int *temp)
+ 
+ static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
+ {
+-	const struct tsens_sensor *s = p;
++	struct tsens_sensor *s = p;
+ 	struct tsens_device *tmdev = s->tmdev;
+ 
+ 	if (tmdev->ops->get_trend)
+@@ -40,9 +40,10 @@ static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
+ 	return -ENOTSUPP;
+ }
+ 
+-static int  __maybe_unused tsens_suspend(struct device *dev)
++static int  __maybe_unused tsens_suspend(void *data)
+ {
+-	struct tsens_device *tmdev = dev_get_drvdata(dev);
++	struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
+ 
+ 	if (tmdev->ops && tmdev->ops->suspend)
+ 		return tmdev->ops->suspend(tmdev);
+@@ -50,9 +51,10 @@ static int  __maybe_unused tsens_suspend(struct device *dev)
+ 	return 0;
+ }
+ 
+-static int __maybe_unused tsens_resume(struct device *dev)
++static int __maybe_unused tsens_resume(void *data)
+ {
+-	struct tsens_device *tmdev = dev_get_drvdata(dev);
++	struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
+ 
+ 	if (tmdev->ops && tmdev->ops->resume)
+ 		return tmdev->ops->resume(tmdev);
+@@ -60,6 +62,30 @@ static int __maybe_unused tsens_resume(struct device *dev)
+ 	return 0;
+ }
+ 
++static int  __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp)
++{
++	struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
++
++	if (tmdev->ops && tmdev->ops->set_trip_temp)
++		return tmdev->ops->set_trip_temp(s, trip, temp);
++
++	return 0;
++}
++
++static int __maybe_unused tsens_activate_trip_type(void *data, int trip,
++					enum thermal_trip_activation_mode mode)
++{
++	struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
++
++	if (tmdev->ops && tmdev->ops->set_trip_activate)
++		return tmdev->ops->set_trip_activate(s, trip, mode);
++
++	return 0;
++}
++
++
+ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
+ 
+ static const struct of_device_id tsens_table[] = {
+@@ -83,6 +109,8 @@ MODULE_DEVICE_TABLE(of, tsens_table);
+ static const struct thermal_zone_of_device_ops tsens_of_ops = {
+ 	.get_temp = tsens_get_temp,
+ 	.get_trend = tsens_get_trend,
++	.set_trip_temp = tsens_set_trip_temp,
++	.set_trip_activate = tsens_activate_trip_type,
+ };
+ 
+ static int tsens_register(struct tsens_device *tmdev)
+@@ -131,7 +159,7 @@ static int tsens_probe(struct platform_device *pdev)
+ 	if (id)
+ 		data = id->data;
+ 	else
+-		data = &data_8960;
++		return -EINVAL;
+ 
+ 	if (data->num_sensors <= 0) {
+ 		dev_err(dev, "invalid number of sensors\n");
+@@ -146,6 +174,9 @@ static int tsens_probe(struct platform_device *pdev)
+ 	tmdev->dev = dev;
+ 	tmdev->num_sensors = data->num_sensors;
+ 	tmdev->ops = data->ops;
++
++	tmdev->tsens_irq = platform_get_irq(pdev, 0);
++
+ 	for (i = 0;  i < tmdev->num_sensors; i++) {
+ 		if (data->hw_ids)
+ 			tmdev->sensor[i].hw_id = data->hw_ids[i];
+diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
+index 31279a2..54bbdc0 100644
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -24,9 +24,12 @@ struct tsens_device;
+ struct tsens_sensor {
+ 	struct tsens_device		*tmdev;
+ 	struct thermal_zone_device	*tzd;
++	struct work_struct		notify_work;
+ 	int				offset;
+ 	int				id;
+ 	int				hw_id;
++	int				calib_data;
++	int				calib_data_backup;
+ 	int				slope;
+ 	u32				status;
+ };
+@@ -41,6 +44,9 @@ struct tsens_sensor {
+  * @suspend: Function to suspend the tsens device
+  * @resume: Function to resume the tsens device
+  * @get_trend: Function to get the thermal/temp trend
++ * @set_trip_temp: Function to set trip temp
++ * @get_trip_temp: Function to get trip temp
++ * @set_trip_activate: Function to activate trip points
+  */
+ struct tsens_ops {
+ 	/* mandatory callbacks */
+@@ -53,6 +59,9 @@ struct tsens_ops {
+ 	int (*suspend)(struct tsens_device *);
+ 	int (*resume)(struct tsens_device *);
+ 	int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
++	int (*set_trip_temp)(void *, int, int);
++	int (*set_trip_activate)(void *, int,
++					enum thermal_trip_activation_mode);
+ };
+ 
+ /**
+@@ -76,11 +85,13 @@ struct tsens_context {
+ struct tsens_device {
+ 	struct device			*dev;
+ 	u32				num_sensors;
++	u32				tsens_irq;
+ 	struct regmap			*map;
+ 	struct regmap_field		*status_field;
+ 	struct tsens_context		ctx;
+ 	bool				trdy;
+ 	const struct tsens_ops		*ops;
++	struct work_struct		tsens_work;
+ 	struct tsens_sensor		sensor[0];
+ };
+ 
+diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
+index 226b0b4..20bd624 100644
+--- a/drivers/thermal/thermal_core.c
++++ b/drivers/thermal/thermal_core.c
+@@ -732,12 +732,48 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
+ 		return sprintf(buf, "passive\n");
+ 	case THERMAL_TRIP_ACTIVE:
+ 		return sprintf(buf, "active\n");
++	case THERMAL_TRIP_CONFIGURABLE_HI:
++		return sprintf(buf, "configurable_hi\n");
++	case THERMAL_TRIP_CONFIGURABLE_LOW:
++		return sprintf(buf, "configurable_low\n");
++	case THERMAL_TRIP_CRITICAL_LOW:
++		return sprintf(buf, "critical_low\n");
+ 	default:
+ 		return sprintf(buf, "unknown\n");
+ 	}
+ }
+ 
+ static ssize_t
++trip_point_type_activate(struct device *dev, struct device_attribute *attr,
++						const char *buf, size_t count)
++{
++	struct thermal_zone_device *tz = to_thermal_zone(dev);
++	int trip, ret;
++	char *enabled = "enabled";
++	char *disabled = "disabled";
++
++	if (!tz->ops->set_trip_activate)
++		return -EPERM;
++
++	if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
++		return -EINVAL;
++
++	if (!strncmp(buf, enabled, strlen(enabled)))
++		ret = tz->ops->set_trip_activate(tz, trip,
++				THERMAL_TRIP_ACTIVATION_ENABLED);
++	else if (!strncmp(buf, disabled, strlen(disabled)))
++		ret = tz->ops->set_trip_activate(tz, trip,
++				THERMAL_TRIP_ACTIVATION_DISABLED);
++	else
++		ret = -EINVAL;
++
++	if (ret)
++		return ret;
++
++	return count;
++}
++
++static ssize_t
+ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
+ 		     const char *buf, size_t count)
+ {
+@@ -1321,7 +1357,7 @@ thermal_cooling_device_weight_store(struct device *dev,
+  */
+ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
+ 				     int trip,
+-				     struct thermal_cooling_device *cdev,
++					struct thermal_cooling_device *cdev,
+ 				     unsigned long upper, unsigned long lower,
+ 				     unsigned int weight)
+ {
+@@ -1772,6 +1808,12 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
+ 		tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO;
+ 		tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
+ 
++		if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS)) {
++			tz->trip_type_attrs[indx].attr.store
++						= trip_point_type_activate;
++			tz->trip_type_attrs[indx].attr.attr.mode |= S_IWUSR;
++		}
++
+ 		device_create_file(&tz->device,
+ 				   &tz->trip_type_attrs[indx].attr);
+ 
+diff --git a/include/linux/thermal.h b/include/linux/thermal.h
+index 511182a..510a087 100644
+--- a/include/linux/thermal.h
++++ b/include/linux/thermal.h
+@@ -77,11 +77,19 @@ enum thermal_device_mode {
+ 	THERMAL_DEVICE_ENABLED,
+ };
+ 
++enum thermal_trip_activation_mode {
++	THERMAL_TRIP_ACTIVATION_DISABLED = 0,
++	THERMAL_TRIP_ACTIVATION_ENABLED,
++};
++
+ enum thermal_trip_type {
+ 	THERMAL_TRIP_ACTIVE = 0,
+ 	THERMAL_TRIP_PASSIVE,
+ 	THERMAL_TRIP_HOT,
+ 	THERMAL_TRIP_CRITICAL,
++	THERMAL_TRIP_CONFIGURABLE_HI,
++	THERMAL_TRIP_CONFIGURABLE_LOW,
++	THERMAL_TRIP_CRITICAL_LOW,
+ };
+ 
+ enum thermal_trend {
+@@ -118,6 +126,8 @@ struct thermal_zone_device_ops {
+ 		enum thermal_trip_type *);
+ 	int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
+ 	int (*set_trip_temp) (struct thermal_zone_device *, int, int);
++	int (*set_trip_activate) (struct thermal_zone_device *, int,
++					enum thermal_trip_activation_mode);
+ 	int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
+ 	int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
+ 	int (*get_crit_temp) (struct thermal_zone_device *, int *);
+@@ -360,6 +370,8 @@ struct thermal_genl_event {
+  *		   temperature.
+  * @set_trip_temp: a pointer to a function that sets the trip temperature on
+  *		   hardware.
++ * @activate_trip_type: a pointer to a function to enable/disable trip
++ *		temperature interrupts
+  */
+ struct thermal_zone_of_device_ops {
+ 	int (*get_temp)(void *, int *);
+@@ -367,6 +379,8 @@ struct thermal_zone_of_device_ops {
+ 	int (*set_trips)(void *, int, int);
+ 	int (*set_emul_temp)(void *, int);
+ 	int (*set_trip_temp)(void *, int, int);
++	int (*set_trip_activate)(void *, int,
++				enum thermal_trip_activation_mode);
+ };
+ 
+ /**
diff --git a/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch b/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch
deleted file mode 100644
index 9f154ae..0000000
--- a/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 3064376aa3e8dae03dc2c5c3c064e2283c4337d8 Mon Sep 17 00:00:00 2001
-From: Pavel Kubelun <be.dissent at gmail.com>
-Date: Tue, 22 Nov 2016 17:37:56 +0300
-Subject: [PATCH 63/69] ipq806x: clk: gcc: add tsens child node
-
-Thermal sensors in ipq806x are inside a Global clock controller.
-Add a child node into it to be used by the TSENS driver.
-
-Signed-off-by: Pavel Kubelun <be.dissent at gmail.com>
----
- drivers/clk/qcom/gcc-ipq806x.c | 10 +++++++++-
- 1 file changed, 9 insertions(+), 1 deletion(-)
-
---- a/drivers/clk/qcom/gcc-ipq806x.c
-+++ b/drivers/clk/qcom/gcc-ipq806x.c
-@@ -970,7 +970,7 @@ static struct clk_branch gsbi1_h_clk = {
- 		.hw.init = &(struct clk_init_data){
- 			.name = "gsbi1_h_clk",
- 			.ops = &clk_branch_ops,
--+			.flags = CLK_IGNORE_UNUSED,
-+			.flags = CLK_IGNORE_UNUSED,
- 		},
- 	},
- };
-@@ -3073,6 +3073,7 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc
- static int gcc_ipq806x_probe(struct platform_device *pdev)
- {
- 	struct device *dev = &pdev->dev;
-+	struct platform_device *tsens;
- 	struct regmap *regmap;
- 	int ret;
- 
-@@ -3102,6 +3103,13 @@ static int gcc_ipq806x_probe(struct plat
- 	regmap_write(regmap, 0x3cf8, 8);
- 	regmap_write(regmap, 0x3d18, 8);
- 
-+	tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
-+					      NULL, 0);
-+	if (IS_ERR(tsens))
-+		return PTR_ERR(tsens);
-+
-+	platform_set_drvdata(pdev, tsens);
-+
- 	return 0;
- }
- 



More information about the lede-commits mailing list