[source] ipq806x: add thermal sensor driver

LEDE Commits lede-commits at lists.infradead.org
Thu Dec 1 06:48:03 PST 2016


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

commit fef6a96d9e9e001a28b6a673c56ed79029457b5d
Author: dissent1 <be.dissent+github at gmail.com>
AuthorDate: Sat Nov 5 16:15:33 2016 +0300

    ipq806x: add thermal sensor driver
    
    Allows to check cpu temperature.
    
    Huge thanks to @hnyman for valuable assistance!
    
    Signed-off-by: Pavel Kubelun <be.dissent at gmail.com>
---
 target/linux/ipq806x/config-4.4                    |   1 +
 .../files/arch/arm/boot/dts/qcom-ipq8065.dtsi      | 109 ++++-
 ...l-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch | 536 +++++++++++++++++++++
 ...-8916-Add-support-for-8916-family-of-SoCs.patch | 165 +++++++
 ...-8974-Add-support-for-8974-family-of-SoCs.patch | 293 +++++++++++
 ...-8960-Add-support-for-8960-family-of-SoCs.patch | 364 ++++++++++++++
 ...ens-8916-mark-PM-functions-__maybe_unused.patch |  46 ++
 ...-2-thermal-of-thermal-Add-devm-version-of.patch | 143 ++++++
 ...-core-export-apis-to-get-slope-and-offset.patch | 101 ++++
 ...-return-error-for-non-word-aligned-access.patch |  42 ++
 ...em-core-fix-error-path-in-nvmem_add_cells.patch |  34 ++
 ...mem-Add-flag-to-export-NVMEM-to-root-only.patch | 101 ++++
 ...tibility-support-for-older-EEPROM-drivers.patch | 181 +++++++
 .../309-clk-gcc-add-tsens-child-node.patch         |  38 ++
 .../310-add-necessary-thermal-data.patch           | 150 ++++++
 ...-qcom-add-gmac-nodes-to-ipq806x-platforms.patch |   2 +-
 16 files changed, 2293 insertions(+), 13 deletions(-)

diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4
index 56a2254..cdb20eb 100644
--- a/target/linux/ipq806x/config-4.4
+++ b/target/linux/ipq806x/config-4.4
@@ -352,6 +352,7 @@ CONFIG_QCOM_SCM=y
 CONFIG_QCOM_SCM_32=y
 # CONFIG_QCOM_SMD is not set
 CONFIG_QCOM_SMEM=y
+CONFIG_QCOM_TSENS=y
 CONFIG_QCOM_WDT=y
 CONFIG_RAS=y
 CONFIG_RATIONAL=y
diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi
index e0ba99a..614610c 100644
--- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi
+++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi
@@ -79,6 +79,92 @@
 		};
 	};
 
+	thermal-zones {
+		cpu-thermal0 {
+			polling-delay-passive = <250>;
+			polling-delay = <1000>;
+
+			thermal-sensors = <&gcc 5>;
+			coefficients = <1132 0>;
+
+			trips {
+				cpu_alert0: trip0 {
+					temperature = <75000>;
+					hysteresis = <2000>;
+					type = "passive";
+				};
+				cpu_crit0: trip1 {
+					temperature = <110000>;
+					hysteresis = <2000>;
+					type = "critical";
+				};
+			};
+		};
+
+		cpu-thermal1 {
+			polling-delay-passive = <250>;
+			polling-delay = <1000>;
+
+			thermal-sensors = <&gcc 6>;
+			coefficients = <1132 0>;
+
+			trips {
+				cpu_alert1: trip0 {
+					temperature = <75000>;
+					hysteresis = <2000>;
+					type = "passive";
+				};
+				cpu_crit1: trip1 {
+					temperature = <110000>;
+					hysteresis = <2000>;
+					type = "critical";
+				};
+			};
+		};
+
+		cpu-thermal2 {
+			polling-delay-passive = <250>;
+			polling-delay = <1000>;
+
+			thermal-sensors = <&gcc 7>;
+			coefficients = <1199 0>;
+
+			trips {
+				cpu_alert2: trip0 {
+					temperature = <75000>;
+					hysteresis = <2000>;
+					type = "passive";
+				};
+				cpu_crit2: trip1 {
+					temperature = <110000>;
+					hysteresis = <2000>;
+					type = "critical";
+				};
+			};
+		};
+
+		cpu-thermal3 {
+			polling-delay-passive = <250>;
+			polling-delay = <1000>;
+
+			thermal-sensors = <&gcc 8>;
+			coefficients = <1132 0>;
+
+			trips {
+				cpu_alert3: trip0 {
+					temperature = <75000>;
+					hysteresis = <2000>;
+					type = "passive";
+				};
+				cpu_crit3: trip1 {
+					temperature = <110000>;
+					hysteresis = <2000>;
+					type = "critical";
+				};
+			};
+		};
+	};
+
 	cpu-pmu {
 		compatible = "qcom,krait-pmu";
 		interrupts = <1 10 0x304>;
@@ -205,8 +291,14 @@
 			reg = <0x00700000 0x1000>;
 			#address-cells = <1>;
 			#size-cells = <1>;
-			stride = <1>;
-			ranges = <0x0 0x00700000 0x1000>;
+			ranges;
+
+			tsens_calib: calib {
+				reg = <0x400 0x10>;
+			};
+			tsens_backup: backup_calib {
+				reg = <0x410 0x10>;
+			};
 		};
 
 		rpm at 108000 {
@@ -687,9 +779,12 @@
 		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>;
+			#thermal-sensor-cells = <1>;
 		};
 
 		lcc: clock-controller at 28000000 {
@@ -704,16 +799,6 @@
 			reg = <0x1a400000 0x100>;
 		};
 
-		tsens: tsens-ipq806x {
-			compatible = "qcom,ipq806x-tsens";
-			reg = <0x900000 0x3678>, <0x700000 0x420>;
-			reg-names = "tsens_physical", "tsens_eeprom_physical";
-			interrupts = <0 178 0>;
-			qcom,sensors = <11>;
-			qcom,tsens_factor = <1000>;
-			qcom,slope = <1176 1176 1154 1176 1111 1132 1132 1199 1132 1199 1132>;
-		};
-
 		qcom,msm-thermal {
 			compatible = "qcom,msm-thermal";
 			qcom,sensor-id = <0>;
diff --git a/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch b/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch
new file mode 100644
index 0000000..ef7e2f4
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch
@@ -0,0 +1,536 @@
+From 9066073c6c27994a30187abf3b674770b4088348 Mon Sep 17 00:00:00 2001
+From: Rajendra Nayak <rnayak at codeaurora.org>
+Date: Thu, 5 May 2016 14:21:39 +0530
+Subject: thermal: qcom: tsens: Add a skeletal TSENS drivers
+
+TSENS is Qualcomms' thermal temperature sensor device. It
+supports reading temperatures from multiple thermal sensors
+present on various QCOM SoCs.
+Calibration data is generally read from a non-volatile memory
+(eeprom) device.
+
+Add a skeleton driver with all the necessary abstractions so
+a variety of qcom device families which support TSENS can
+add driver extensions.
+
+Also add the required device tree bindings which can be used
+to describe the TSENS device in DT.
+
+Signed-off-by: Rajendra Nayak <rnayak at codeaurora.org>
+Reviewed-by: Lina Iyer <lina.iyer at linaro.org>
+Signed-off-by: Eduardo Valentin <edubezval at gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang at intel.com>
+---
+ .../devicetree/bindings/thermal/qcom-tsens.txt     |  21 +++
+ drivers/thermal/Kconfig                            |   5 +
+ drivers/thermal/Makefile                           |   1 +
+ drivers/thermal/qcom/Kconfig                       |  11 ++
+ drivers/thermal/qcom/Makefile                      |   2 +
+ drivers/thermal/qcom/tsens-common.c                | 141 +++++++++++++++
+ drivers/thermal/qcom/tsens.c                       | 195 +++++++++++++++++++++
+ drivers/thermal/qcom/tsens.h                       |  90 ++++++++++
+ 8 files changed, 466 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+ create mode 100644 drivers/thermal/qcom/Kconfig
+ create mode 100644 drivers/thermal/qcom/Makefile
+ create mode 100644 drivers/thermal/qcom/tsens-common.c
+ create mode 100644 drivers/thermal/qcom/tsens.c
+ create mode 100644 drivers/thermal/qcom/tsens.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+@@ -0,0 +1,21 @@
++* QCOM SoC Temperature Sensor (TSENS)
++
++Required properties:
++- compatible :
++ - "qcom,msm8916-tsens" : For 8916 Family of SoCs
++ - "qcom,msm8974-tsens" : For 8974 Family of SoCs
++ - "qcom,msm8996-tsens" : For 8996 Family of SoCs
++
++- reg: Address range of the thermal registers
++- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
++- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
++nvmem cells
++
++Example:
++tsens: thermal-sensor at 900000 {
++		compatible = "qcom,msm8916-tsens";
++		reg = <0x4a8000 0x2000>;
++		nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
++		nvmem-cell-names = "caldata", "calsel";
++		#thermal-sensor-cells = <1>;
++	};
+--- a/drivers/thermal/Kconfig
++++ b/drivers/thermal/Kconfig
+@@ -391,4 +391,9 @@ config QCOM_SPMI_TEMP_ALARM
+ 	  real time die temperature if an ADC is present or an estimate of the
+ 	  temperature based upon the over temperature stage value.
+ 
++menu "Qualcomm thermal drivers"
++depends on (ARCH_QCOM && OF) || COMPILE_TEST
++source "drivers/thermal/qcom/Kconfig"
++endmenu
++
+ endif
+--- a/drivers/thermal/Makefile
++++ b/drivers/thermal/Makefile
+@@ -48,3 +48,4 @@ obj-$(CONFIG_INTEL_PCH_THERMAL)	+= intel
+ obj-$(CONFIG_ST_THERMAL)	+= st/
+ obj-$(CONFIG_TEGRA_SOCTHERM)	+= tegra_soctherm.o
+ obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
++obj-$(CONFIG_QCOM_TSENS)	+= qcom/
+--- /dev/null
++++ b/drivers/thermal/qcom/Kconfig
+@@ -0,0 +1,11 @@
++config QCOM_TSENS
++	tristate "Qualcomm TSENS Temperature Alarm"
++	depends on THERMAL
++	depends on QCOM_QFPROM
++	depends on ARCH_QCOM || COMPILE_TEST
++	help
++	  This enables the thermal sysfs driver for the TSENS device. It shows
++	  up in Sysfs as a thermal zone with multiple trip points. Disabling the
++	  thermal zone device via the mode file results in disabling the sensor.
++	  Also able to set threshold temperature for both hot and cold and update
++	  when a threshold is reached.
+--- /dev/null
++++ b/drivers/thermal/qcom/Makefile
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_QCOM_TSENS)	+= qcom_tsens.o
++qcom_tsens-y			+= tsens.o tsens-common.o
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-common.c
+@@ -0,0 +1,141 @@
++/*
++ * 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/err.h>
++#include <linux/io.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include "tsens.h"
++
++#define S0_ST_ADDR		0x1030
++#define SN_ADDR_OFFSET		0x4
++#define SN_ST_TEMP_MASK		0x3ff
++#define CAL_DEGC_PT1		30
++#define CAL_DEGC_PT2		120
++#define SLOPE_FACTOR		1000
++#define SLOPE_DEFAULT		3200
++
++char *qfprom_read(struct device *dev, const char *cname)
++{
++	struct nvmem_cell *cell;
++	ssize_t data;
++	char *ret;
++
++	cell = nvmem_cell_get(dev, cname);
++	if (IS_ERR(cell))
++		return ERR_CAST(cell);
++
++	ret = nvmem_cell_read(cell, &data);
++	nvmem_cell_put(cell);
++
++	return ret;
++}
++
++/*
++ * Use this function on devices where slope and offset calculations
++ * depend on calibration data read from qfprom. On others the slope
++ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
++ * resp.
++ */
++void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
++			     u32 *p2, u32 mode)
++{
++	int i;
++	int num, den;
++
++	for (i = 0; i < tmdev->num_sensors; i++) {
++		dev_dbg(tmdev->dev,
++			"sensor%d - data_point1:%#x data_point2:%#x\n",
++			i, p1[i], p2[i]);
++
++		tmdev->sensor[i].slope = SLOPE_DEFAULT;
++		if (mode == TWO_PT_CALIB) {
++			/*
++			 * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
++			 *	temp_120_degc - temp_30_degc (x2 - x1)
++			 */
++			num = p2[i] - p1[i];
++			num *= SLOPE_FACTOR;
++			den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
++			tmdev->sensor[i].slope = num / den;
++		}
++
++		tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
++				(CAL_DEGC_PT1 *
++				tmdev->sensor[i].slope);
++		dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
++	}
++}
++
++static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
++{
++	int degc, num, den;
++
++	num = (adc_code * SLOPE_FACTOR) - s->offset;
++	den = s->slope;
++
++	if (num > 0)
++		degc = num + (den / 2);
++	else if (num < 0)
++		degc = num - (den / 2);
++	else
++		degc = num;
++
++	degc /= den;
++
++	return degc;
++}
++
++int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
++{
++	struct tsens_sensor *s = &tmdev->sensor[id];
++	u32 code;
++	unsigned int sensor_addr;
++	int last_temp = 0, ret;
++
++	sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
++	ret = regmap_read(tmdev->map, sensor_addr, &code);
++	if (ret)
++		return ret;
++	last_temp = code & SN_ST_TEMP_MASK;
++
++	*temp = code_to_degc(last_temp, s) * 1000;
++
++	return 0;
++}
++
++static const struct regmap_config tsens_config = {
++	.reg_bits	= 32,
++	.val_bits	= 32,
++	.reg_stride	= 4,
++};
++
++int __init init_common(struct tsens_device *tmdev)
++{
++	void __iomem *base;
++
++	base = of_iomap(tmdev->dev->of_node, 0);
++	if (IS_ERR(base))
++		return -EINVAL;
++
++	tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
++	if (!tmdev->map) {
++		iounmap(base);
++		return -ENODEV;
++	}
++
++	return 0;
++}
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens.c
+@@ -0,0 +1,195 @@
++/*
++ * 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/err.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++#include "tsens.h"
++
++static int tsens_get_temp(void *data, int *temp)
++{
++	const struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
++
++	return tmdev->ops->get_temp(tmdev, s->id, temp);
++}
++
++static int tsens_get_trend(void *data, long *temp)
++{
++	const struct tsens_sensor *s = data;
++	struct tsens_device *tmdev = s->tmdev;
++
++	if (tmdev->ops->get_trend)
++		return tmdev->ops->get_trend(tmdev, s->id, temp);
++
++	return -ENOTSUPP;
++}
++
++static int tsens_suspend(struct device *dev)
++{
++	struct tsens_device *tmdev = dev_get_drvdata(dev);
++
++	if (tmdev->ops && tmdev->ops->suspend)
++		return tmdev->ops->suspend(tmdev);
++
++	return 0;
++}
++
++static int tsens_resume(struct device *dev)
++{
++	struct tsens_device *tmdev = dev_get_drvdata(dev);
++
++	if (tmdev->ops && tmdev->ops->resume)
++		return tmdev->ops->resume(tmdev);
++
++	return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
++
++static const struct of_device_id tsens_table[] = {
++	{
++		.compatible = "qcom,msm8916-tsens",
++	}, {
++		.compatible = "qcom,msm8974-tsens",
++	},
++	{}
++};
++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,
++};
++
++static int tsens_register(struct tsens_device *tmdev)
++{
++	int i;
++	struct thermal_zone_device *tzd;
++	u32 *hw_id, n = tmdev->num_sensors;
++
++	hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
++	if (!hw_id)
++		return -ENOMEM;
++
++	for (i = 0;  i < tmdev->num_sensors; i++) {
++		tmdev->sensor[i].tmdev = tmdev;
++		tmdev->sensor[i].id = i;
++		tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
++							   &tmdev->sensor[i],
++							   &tsens_of_ops);
++		if (IS_ERR(tzd))
++			continue;
++		tmdev->sensor[i].tzd = tzd;
++		if (tmdev->ops->enable)
++			tmdev->ops->enable(tmdev, i);
++	}
++	return 0;
++}
++
++static int tsens_probe(struct platform_device *pdev)
++{
++	int ret, i;
++	struct device *dev;
++	struct device_node *np;
++	struct tsens_sensor *s;
++	struct tsens_device *tmdev;
++	const struct tsens_data *data;
++	const struct of_device_id *id;
++
++	if (pdev->dev.of_node)
++		dev = &pdev->dev;
++	else
++		dev = pdev->dev.parent;
++
++	np = dev->of_node;
++
++	id = of_match_node(tsens_table, np);
++	if (!id)
++		return -EINVAL;
++
++	data = id->data;
++
++	if (data->num_sensors <= 0) {
++		dev_err(dev, "invalid number of sensors\n");
++		return -EINVAL;
++	}
++
++	tmdev = devm_kzalloc(dev, sizeof(*tmdev) +
++			     data->num_sensors * sizeof(*s), GFP_KERNEL);
++	if (!tmdev)
++		return -ENOMEM;
++
++	tmdev->dev = dev;
++	tmdev->num_sensors = data->num_sensors;
++	tmdev->ops = data->ops;
++	for (i = 0;  i < tmdev->num_sensors; i++) {
++		if (data->hw_ids)
++			tmdev->sensor[i].hw_id = data->hw_ids[i];
++		else
++			tmdev->sensor[i].hw_id = i;
++	}
++
++	if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
++		return -EINVAL;
++
++	ret = tmdev->ops->init(tmdev);
++	if (ret < 0) {
++		dev_err(dev, "tsens init failed\n");
++		return ret;
++	}
++
++	if (tmdev->ops->calibrate) {
++		ret = tmdev->ops->calibrate(tmdev);
++		if (ret < 0) {
++			dev_err(dev, "tsens calibration failed\n");
++			return ret;
++		}
++	}
++
++	ret = tsens_register(tmdev);
++
++	platform_set_drvdata(pdev, tmdev);
++
++	return ret;
++}
++
++static int tsens_remove(struct platform_device *pdev)
++{
++	struct tsens_device *tmdev = platform_get_drvdata(pdev);
++
++	if (tmdev->ops->disable)
++		tmdev->ops->disable(tmdev);
++
++	return 0;
++}
++
++static struct platform_driver tsens_driver = {
++	.probe = tsens_probe,
++	.remove = tsens_remove,
++	.driver = {
++		.name = "qcom-tsens",
++		.pm	= &tsens_pm_ops,
++		.of_match_table = tsens_table,
++	},
++};
++module_platform_driver(tsens_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
++MODULE_ALIAS("platform:qcom-tsens");
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens.h
+@@ -0,0 +1,90 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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.
++ */
++#ifndef __QCOM_TSENS_H__
++#define __QCOM_TSENS_H__
++
++#define ONE_PT_CALIB		0x1
++#define ONE_PT_CALIB2		0x2
++#define TWO_PT_CALIB		0x3
++
++struct tsens_device;
++
++struct tsens_sensor {
++	struct tsens_device		*tmdev;
++	struct thermal_zone_device	*tzd;
++	int				offset;
++	int				id;
++	int				hw_id;
++	int				slope;
++	u32				status;
++};
++
++/**
++ * struct tsens_ops - operations as supported by the tsens device
++ * @init: Function to initialize the tsens device
++ * @calibrate: Function to calibrate the tsens device
++ * @get_temp: Function which returns the temp in millidegC
++ * @enable: Function to enable (clocks/power) tsens device
++ * @disable: Function to disable the tsens device
++ * @suspend: Function to suspend the tsens device
++ * @resume: Function to resume the tsens device
++ * @get_trend: Function to get the thermal/temp trend
++ */
++struct tsens_ops {
++	/* mandatory callbacks */
++	int (*init)(struct tsens_device *);
++	int (*calibrate)(struct tsens_device *);
++	int (*get_temp)(struct tsens_device *, int, int *);
++	/* optional callbacks */
++	int (*enable)(struct tsens_device *, int);
++	void (*disable)(struct tsens_device *);
++	int (*suspend)(struct tsens_device *);
++	int (*resume)(struct tsens_device *);
++	int (*get_trend)(struct tsens_device *, int, long *);
++};
++
++/**
++ * struct tsens_data - tsens instance specific data
++ * @num_sensors: Max number of sensors supported by platform
++ * @ops: operations the tsens instance supports
++ * @hw_ids: Subset of sensors ids supported by platform, if not the first n
++ */
++struct tsens_data {
++	const u32		num_sensors;
++	const struct tsens_ops	*ops;
++	unsigned int		*hw_ids;
++};
++
++/* Registers to be saved/restored across a context loss */
++struct tsens_context {
++	int	threshold;
++	int	control;
++};
++
++struct tsens_device {
++	struct device			*dev;
++	u32				num_sensors;
++	struct regmap			*map;
++	struct regmap_field		*status_field;
++	struct tsens_context		ctx;
++	bool				trdy;
++	const struct tsens_ops		*ops;
++	struct tsens_sensor		sensor[0];
++};
++
++char *qfprom_read(struct device *, const char *);
++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 *);
++
++#endif /* __QCOM_TSENS_H__ */
diff --git a/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch
new file mode 100644
index 0000000..d076196
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch
@@ -0,0 +1,165 @@
+From 840a5bd3ed3fdd62456d4d26c3128ec10496555b Mon Sep 17 00:00:00 2001
+From: Rajendra Nayak <rnayak at codeaurora.org>
+Date: Thu, 5 May 2016 14:21:40 +0530
+Subject: thermal: qcom: tsens-8916: Add support for 8916 family of SoCs
+
+Add support to calibrate sensors on 8916 family and also add common
+functions to read temperature from sensors (This can be reused on
+other SoCs having similar TSENS device)
+The calibration data is read from eeprom using the generic nvmem
+framework apis.
+
+Based on the original code by Siddartha Mohanadoss and Stephen Boyd.
+
+Signed-off-by: Rajendra Nayak <rnayak at codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval at gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang at intel.com>
+---
+ drivers/thermal/qcom/Makefile     |   2 +-
+ drivers/thermal/qcom/tsens-8916.c | 113 ++++++++++++++++++++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c      |   1 +
+ drivers/thermal/qcom/tsens.h      |   2 +
+ 4 files changed, 117 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/thermal/qcom/tsens-8916.c
+
+--- a/drivers/thermal/qcom/Makefile
++++ b/drivers/thermal/qcom/Makefile
+@@ -1,2 +1,2 @@
+ obj-$(CONFIG_QCOM_TSENS)	+= qcom_tsens.o
+-qcom_tsens-y			+= tsens.o tsens-common.o
++qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-8916.c
+@@ -0,0 +1,113 @@
++/*
++ * 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 "tsens.h"
++
++/* eeprom layout data for 8916 */
++#define BASE0_MASK	0x0000007f
++#define BASE1_MASK	0xfe000000
++#define BASE0_SHIFT	0
++#define BASE1_SHIFT	25
++
++#define S0_P1_MASK	0x00000f80
++#define S1_P1_MASK	0x003e0000
++#define S2_P1_MASK	0xf8000000
++#define S3_P1_MASK	0x000003e0
++#define S4_P1_MASK	0x000f8000
++
++#define S0_P2_MASK	0x0001f000
++#define S1_P2_MASK	0x07c00000
++#define S2_P2_MASK	0x0000001f
++#define S3_P2_MASK	0x00007c00
++#define S4_P2_MASK	0x01f00000
++
++#define S0_P1_SHIFT	7
++#define S1_P1_SHIFT	17
++#define S2_P1_SHIFT	27
++#define S3_P1_SHIFT	5
++#define S4_P1_SHIFT	15
++
++#define S0_P2_SHIFT	12
++#define S1_P2_SHIFT	22
++#define S2_P2_SHIFT	0
++#define S3_P2_SHIFT	10
++#define S4_P2_SHIFT	20
++
++#define CAL_SEL_MASK	0xe0000000
++#define CAL_SEL_SHIFT	29
++
++static int calibrate_8916(struct tsens_device *tmdev)
++{
++	int base0 = 0, base1 = 0, i;
++	u32 p1[5], p2[5];
++	int mode = 0;
++	u32 *qfprom_cdata, *qfprom_csel;
++
++	qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
++	if (IS_ERR(qfprom_cdata))
++		return PTR_ERR(qfprom_cdata);
++
++	qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel");
++	if (IS_ERR(qfprom_csel))
++		return PTR_ERR(qfprom_csel);
++
++	mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
++	dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);
++
++	switch (mode) {
++	case TWO_PT_CALIB:
++		base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
++		p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
++		p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
++		p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
++		p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
++		p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
++		for (i = 0; i < tmdev->num_sensors; i++)
++			p2[i] = ((base1 + p2[i]) << 3);
++		/* Fall through */
++	case ONE_PT_CALIB2:
++		base0 = (qfprom_cdata[0] & BASE0_MASK);
++		p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
++		p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
++		p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
++		p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
++		p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
++		for (i = 0; i < tmdev->num_sensors; i++)
++			p1[i] = (((base0) + p1[i]) << 3);
++		break;
++	default:
++		for (i = 0; i < tmdev->num_sensors; i++) {
++			p1[i] = 500;
++			p2[i] = 780;
++		}
++		break;
++	}
++
++	compute_intercept_slope(tmdev, p1, p2, mode);
++
++	return 0;
++}
++
++const struct tsens_ops ops_8916 = {
++	.init		= init_common,
++	.calibrate	= calibrate_8916,
++	.get_temp	= get_temp_common,
++};
++
++const struct tsens_data data_8916 = {
++	.num_sensors	= 5,
++	.ops		= &ops_8916,
++	.hw_ids		= (unsigned int []){0, 1, 2, 4, 5 },
++};
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -65,6 +65,7 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t
+ static const struct of_device_id tsens_table[] = {
+ 	{
+ 		.compatible = "qcom,msm8916-tsens",
++		.data = &data_8916,
+ 	}, {
+ 		.compatible = "qcom,msm8974-tsens",
+ 	},
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -87,4 +87,6 @@ void compute_intercept_slope(struct tsen
+ int init_common(struct tsens_device *);
+ int get_temp_common(struct tsens_device *, int, int *);
+ 
++extern const struct tsens_data data_8916;
++
+ #endif /* __QCOM_TSENS_H__ */
diff --git a/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch
new file mode 100644
index 0000000..671f461
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch
@@ -0,0 +1,293 @@
+From 5e6703bd2d83548998848865cb9a9a795f31a311 Mon Sep 17 00:00:00 2001
+From: Rajendra Nayak <rnayak at codeaurora.org>
+Date: Thu, 5 May 2016 14:21:41 +0530
+Subject: thermal: qcom: tsens-8974: Add support for 8974 family of SoCs
+
+Add .calibrate support for 8974 family as part of tsens_ops.
+
+Based on the original code by Siddartha Mohanadoss and Stephen Boyd.
+
+Signed-off-by: Rajendra Nayak <rnayak at codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval at gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang at intel.com>
+---
+ drivers/thermal/qcom/Makefile     |   2 +-
+ drivers/thermal/qcom/tsens-8974.c | 244 ++++++++++++++++++++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c      |   1 +
+ drivers/thermal/qcom/tsens.h      |   2 +-
+ 4 files changed, 247 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/thermal/qcom/tsens-8974.c
+
+--- a/drivers/thermal/qcom/Makefile
++++ b/drivers/thermal/qcom/Makefile
+@@ -1,2 +1,2 @@
+ obj-$(CONFIG_QCOM_TSENS)	+= qcom_tsens.o
+-qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o
++qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-8974.c
+@@ -0,0 +1,244 @@
++/*
++ * 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 "tsens.h"
++
++/* eeprom layout data for 8974 */
++#define BASE1_MASK		0xff
++#define S0_P1_MASK		0x3f00
++#define S1_P1_MASK		0xfc000
++#define S2_P1_MASK		0x3f00000
++#define S3_P1_MASK		0xfc000000
++#define S4_P1_MASK		0x3f
++#define S5_P1_MASK		0xfc0
++#define S6_P1_MASK		0x3f000
++#define S7_P1_MASK		0xfc0000
++#define S8_P1_MASK		0x3f000000
++#define S8_P1_MASK_BKP		0x3f
++#define S9_P1_MASK		0x3f
++#define S9_P1_MASK_BKP		0xfc0
++#define S10_P1_MASK		0xfc0
++#define S10_P1_MASK_BKP		0x3f000
++#define CAL_SEL_0_1		0xc0000000
++#define CAL_SEL_2		0x40000000
++#define CAL_SEL_SHIFT		30
++#define CAL_SEL_SHIFT_2		28
++
++#define S0_P1_SHIFT		8
++#define S1_P1_SHIFT		14
++#define S2_P1_SHIFT		20
++#define S3_P1_SHIFT		26
++#define S5_P1_SHIFT		6
++#define S6_P1_SHIFT		12
++#define S7_P1_SHIFT		18
++#define S8_P1_SHIFT		24
++#define S9_P1_BKP_SHIFT		6
++#define S10_P1_SHIFT		6
++#define S10_P1_BKP_SHIFT	12
++
++#define BASE2_SHIFT		12
++#define BASE2_BKP_SHIFT		18
++#define S0_P2_SHIFT		20
++#define S0_P2_BKP_SHIFT		26
++#define S1_P2_SHIFT		26
++#define S2_P2_BKP_SHIFT		6
++#define S3_P2_SHIFT		6
++#define S3_P2_BKP_SHIFT		12
++#define S4_P2_SHIFT		12
++#define S4_P2_BKP_SHIFT		18
++#define S5_P2_SHIFT		18
++#define S5_P2_BKP_SHIFT		24
++#define S6_P2_SHIFT		24
++#define S7_P2_BKP_SHIFT		6
++#define S8_P2_SHIFT		6
++#define S8_P2_BKP_SHIFT		12
++#define S9_P2_SHIFT		12
++#define S9_P2_BKP_SHIFT		18
++#define S10_P2_SHIFT		18
++#define S10_P2_BKP_SHIFT	24
++
++#define BASE2_MASK		0xff000
++#define BASE2_BKP_MASK		0xfc0000
++#define S0_P2_MASK		0x3f00000
++#define S0_P2_BKP_MASK		0xfc000000
++#define S1_P2_MASK		0xfc000000
++#define S1_P2_BKP_MASK		0x3f
++#define S2_P2_MASK		0x3f
++#define S2_P2_BKP_MASK		0xfc0
++#define S3_P2_MASK		0xfc0
++#define S3_P2_BKP_MASK		0x3f000
++#define S4_P2_MASK		0x3f000
++#define S4_P2_BKP_MASK		0xfc0000
++#define S5_P2_MASK		0xfc0000
++#define S5_P2_BKP_MASK		0x3f000000
++#define S6_P2_MASK		0x3f000000
++#define S6_P2_BKP_MASK		0x3f
++#define S7_P2_MASK		0x3f
++#define S7_P2_BKP_MASK		0xfc0
++#define S8_P2_MASK		0xfc0
++#define S8_P2_BKP_MASK		0x3f000
++#define S9_P2_MASK		0x3f000
++#define S9_P2_BKP_MASK		0xfc0000
++#define S10_P2_MASK		0xfc0000
++#define S10_P2_BKP_MASK		0x3f000000
++
++#define BKP_SEL			0x3
++#define BKP_REDUN_SEL		0xe0000000
++#define BKP_REDUN_SHIFT		29
++
++#define BIT_APPEND		0x3
++
++static int calibrate_8974(struct tsens_device *tmdev)
++{
++	int base1 = 0, base2 = 0, i;
++	u32 p1[11], p2[11];
++	int mode = 0;
++	u32 *calib, *bkp;
++	u32 calib_redun_sel;
++
++	calib = (u32 *)qfprom_read(tmdev->dev, "calib");
++	if (IS_ERR(calib))
++		return PTR_ERR(calib);
++
++	bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup");
++	if (IS_ERR(bkp))
++		return PTR_ERR(bkp);
++
++	calib_redun_sel =  bkp[1] & BKP_REDUN_SEL;
++	calib_redun_sel >>= BKP_REDUN_SHIFT;
++
++	if (calib_redun_sel == BKP_SEL) {
++		mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
++		mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
++
++		switch (mode) {
++		case TWO_PT_CALIB:
++			base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
++			p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
++			p2[1] = (bkp[3] & S1_P2_BKP_MASK);
++			p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
++			p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
++			p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
++			p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
++			p2[6] = (calib[5] & S6_P2_BKP_MASK);
++			p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
++			p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
++			p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
++			p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
++			/* Fall through */
++		case ONE_PT_CALIB:
++		case ONE_PT_CALIB2:
++			base1 = bkp[0] & BASE1_MASK;
++			p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
++			p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
++			p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
++			p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
++			p1[4] = (bkp[1] & S4_P1_MASK);
++			p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
++			p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
++			p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
++			p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
++			p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
++			p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
++			break;
++		}
++	} else {
++		mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
++		mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
++
++		switch (mode) {
++		case TWO_PT_CALIB:
++			base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
++			p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
++			p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
++			p2[2] = (calib[3] & S2_P2_MASK);
++			p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
++			p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
++			p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
++			p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
++			p2[7] = (calib[4] & S7_P2_MASK);
++			p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
++			p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
++			p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
++			/* Fall through */
++		case ONE_PT_CALIB:
++		case ONE_PT_CALIB2:
++			base1 = calib[0] & BASE1_MASK;
++			p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
++			p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
++			p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
++			p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
++			p1[4] = (calib[1] & S4_P1_MASK);
++			p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
++			p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
++			p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
++			p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
++			p1[9] = (calib[2] & S9_P1_MASK);
++			p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
++			break;
++		}
++	}
++
++	switch (mode) {
++	case ONE_PT_CALIB:
++		for (i = 0; i < tmdev->num_sensors; i++)
++			p1[i] += (base1 << 2) | BIT_APPEND;
++		break;
++	case TWO_PT_CALIB:
++		for (i = 0; i < tmdev->num_sensors; i++) {
++			p2[i] += base2;
++			p2[i] <<= 2;
++			p2[i] |= BIT_APPEND;
++		}
++		/* Fall through */
++	case ONE_PT_CALIB2:
++		for (i = 0; i < tmdev->num_sensors; i++) {
++			p1[i] += base1;
++			p1[i] <<= 2;
++			p1[i] |= BIT_APPEND;
++		}
++		break;
++	default:
++		for (i = 0; i < tmdev->num_sensors; i++)
++			p2[i] = 780;
++		p1[0] = 502;
++		p1[1] = 509;
++		p1[2] = 503;
++		p1[3] = 509;
++		p1[4] = 505;
++		p1[5] = 509;
++		p1[6] = 507;
++		p1[7] = 510;
++		p1[8] = 508;
++		p1[9] = 509;
++		p1[10] = 508;
++		break;
++	}
++
++	compute_intercept_slope(tmdev, p1, p2, mode);
++
++	return 0;
++}
++
++const struct tsens_ops ops_8974 = {
++	.init		= init_common,
++	.calibrate	= calibrate_8974,
++	.get_temp	= get_temp_common,
++};
++
++const struct tsens_data data_8974 = {
++	.num_sensors	= 11,
++	.ops		= &ops_8974,
++};
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -68,6 +68,7 @@ static const struct of_device_id tsens_t
+ 		.data = &data_8916,
+ 	}, {
+ 		.compatible = "qcom,msm8974-tsens",
++		.data = &data_8974,
+ 	},
+ 	{}
+ };
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen
+ int init_common(struct tsens_device *);
+ int get_temp_common(struct tsens_device *, int, int *);
+ 
+-extern const struct tsens_data data_8916;
++extern const struct tsens_data data_8916, data_8974;
+ 
+ #endif /* __QCOM_TSENS_H__ */
diff --git a/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch
new file mode 100644
index 0000000..05490cd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch
@@ -0,0 +1,364 @@
+From 20d4fd84bf524ad91e2cc3e4ab4020c27cfc0081 Mon Sep 17 00:00:00 2001
+From: Rajendra Nayak <rnayak at codeaurora.org>
+Date: Thu, 5 May 2016 14:21:43 +0530
+Subject: thermal: qcom: tsens-8960: Add support for 8960 family of SoCs
+
+8960 family of SoCs have the TSENS device as part of GCC, hence
+the driver probes the virtual child device created by GCC and
+uses the parent to extract all DT properties and reuses the GCC
+regmap.
+
+Also GCC/TSENS are part of a  domain thats not always ON.
+Hence add .suspend and .resume hooks to save and restore some of
+the inited register context.
+
+Also 8960 family have some of the TSENS init sequence thats
+required to be done by the HLOS driver (some later versions of TSENS
+do not export these registers to non-secure world, and hence need
+these initializations to be done by secure bootloaders)
+
+8660 from the same family has just one sensor and hence some register
+offset/layout differences which need special handling in the driver.
+
+Based on the original code from Siddartha Mohanadoss, Stephen Boyd and
+Narendran Rajan.
+
+Signed-off-by: Rajendra Nayak <rnayak at codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval at gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang at intel.com>
+---
+ drivers/thermal/qcom/Makefile     |   2 +-
+ drivers/thermal/qcom/tsens-8960.c | 292 ++++++++++++++++++++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c      |   8 +-
+ drivers/thermal/qcom/tsens.h      |   2 +-
+ 4 files changed, 298 insertions(+), 6 deletions(-)
+ create mode 100644 drivers/thermal/qcom/tsens-8960.c
+
+--- a/drivers/thermal/qcom/Makefile
++++ b/drivers/thermal/qcom/Makefile
+@@ -1,2 +1,2 @@
+ obj-$(CONFIG_QCOM_TSENS)	+= qcom_tsens.o
+-qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o
++qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -0,0 +1,292 @@
++/*
++ * 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 "tsens.h"
++
++#define CAL_MDEGC		30000
++
++#define CONFIG_ADDR		0x3640
++#define CONFIG_ADDR_8660	0x3620
++/* CONFIG_ADDR bitmasks */
++#define CONFIG			0x9b
++#define CONFIG_MASK		0xf
++#define CONFIG_8660		1
++#define CONFIG_SHIFT_8660	28
++#define CONFIG_MASK_8660	(3 << CONFIG_SHIFT_8660)
++
++#define STATUS_CNTL_ADDR_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 SLP_CLK_ENA_8660	BIT(24)
++#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_LIMIT_SHIFT	24
++#define THRESHOLD_MIN_LIMIT_SHIFT	16
++#define THRESHOLD_UPPER_LIMIT_SHIFT	8
++#define THRESHOLD_LOWER_LIMIT_SHIFT	0
++
++/* Initial temperature threshold values */
++#define LOWER_LIMIT_TH		0x50
++#define UPPER_LIMIT_TH		0xdf
++#define MIN_LIMIT_TH		0x0
++#define MAX_LIMIT_TH		0xff
++
++#define S0_STATUS_ADDR		0x3628
++#define INT_STATUS_ADDR		0x363c
++#define TRDY_MASK		BIT(7)
++#define TIMEOUT_US		100
++
++static int suspend_8960(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;
++
++	if (tmdev->num_sensors > 1)
++		mask = SLP_CLK_ENA | EN;
++	else
++		mask = SLP_CLK_ENA_8660 | EN;
++
++	ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static int resume_8960(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;
++
++	/*
++	 * Separate CONFIG restore is not needed only for 8660 as
++	 * config is part of CTRL Addr and its restored as such
++	 */
++	if (tmdev->num_sensors > 1) {
++		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 int enable_8960(struct tsens_device *tmdev, int id)
++{
++	int ret;
++	u32 reg, mask;
++
++	ret = regmap_read(tmdev->map, CNTL_ADDR, &reg);
++	if (ret)
++		return ret;
++
++	mask = BIT(id + SENSOR0_SHIFT);
++	ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
++	if (ret)
++		return ret;
++
++	if (tmdev->num_sensors > 1)
++		reg |= mask | SLP_CLK_ENA | EN;
++	else
++		reg |= mask | SLP_CLK_ENA_8660 | EN;
++
++	ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static void disable_8960(struct tsens_device *tmdev)
++{
++	int ret;
++	u32 reg_cntl;
++	u32 mask;
++
++	mask = GENMASK(tmdev->num_sensors - 1, 0);
++	mask <<= SENSOR0_SHIFT;
++	mask |= EN;
++
++	ret = regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
++	if (ret)
++		return;
++
++	reg_cntl &= ~mask;
++
++	if (tmdev->num_sensors > 1)
++		reg_cntl &= ~SLP_CLK_ENA;
++	else
++		reg_cntl &= ~SLP_CLK_ENA_8660;
++
++	regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++}
++
++static int init_8960(struct tsens_device *tmdev)
++{
++	int ret, i;
++	u32 reg_cntl;
++
++	tmdev->map = dev_get_regmap(tmdev->dev, NULL);
++	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 >= 5)
++			tmdev->sensor[i].status = S0_STATUS_ADDR + 40;
++		tmdev->sensor[i].status += i * 4;
++	}
++
++	reg_cntl = SW_RST;
++	ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
++	if (ret)
++		return ret;
++
++	if (tmdev->num_sensors > 1) {
++		reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
++		reg_cntl &= ~SW_RST;
++		ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
++					 CONFIG_MASK, CONFIG);
++	} else {
++		reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
++		reg_cntl &= ~CONFIG_MASK_8660;
++		reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
++	}
++
++	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_8960(struct tsens_device *tmdev)
++{
++	int i;
++	char *data;
++
++	ssize_t num_read = tmdev->num_sensors;
++	struct tsens_sensor *s = tmdev->sensor;
++
++	data = qfprom_read(tmdev->dev, "calib");
++	if (IS_ERR(data))
++		data = qfprom_read(tmdev->dev, "calib_backup");
++	if (IS_ERR(data))
++		return PTR_ERR(data);
++
++	for (i = 0; i < num_read; i++, s++)
++		s->offset = data[i];
++
++	return 0;
++}
++
++/* Temperature on y axis and ADC-code on x-axis */
++static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
++{
++	int slope, offset;
++
++	slope = thermal_zone_get_slope(s->tzd);
++	offset = CAL_MDEGC - slope * s->offset;
++
++	return adc_code * slope + offset;
++}
++
++static int get_temp_8960(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_mdegC(code, s);
++		return 0;
++	} while (time_before(jiffies, timeout));
++
++	return -ETIMEDOUT;
++}
++
++const struct tsens_ops ops_8960 = {
++	.init		= init_8960,
++	.calibrate	= calibrate_8960,
++	.get_temp	= get_temp_8960,
++	.enable		= enable_8960,
++	.disable	= disable_8960,
++	.suspend	= suspend_8960,
++	.resume		= resume_8960,
++};
++
++const struct tsens_data data_8960 = {
++	.num_sensors	= 11,
++	.ops		= &ops_8960,
++};
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -122,10 +122,10 @@ static int tsens_probe(struct platform_d
+ 	np = dev->of_node;
+ 
+ 	id = of_match_node(tsens_table, np);
+-	if (!id)
+-		return -EINVAL;
+-
+-	data = id->data;
++	if (id)
++		data = id->data;
++	else
++		data = &data_8960;
+ 
+ 	if (data->num_sensors <= 0) {
+ 		dev_err(dev, "invalid number of sensors\n");
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsen
+ int init_common(struct tsens_device *);
+ int get_temp_common(struct tsens_device *, int, int *);
+ 
+-extern const struct tsens_data data_8916, data_8974;
++extern const struct tsens_data data_8916, data_8974, data_8960;
+ 
+ #endif /* __QCOM_TSENS_H__ */
diff --git a/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch b/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch
new file mode 100644
index 0000000..39d2173
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch
@@ -0,0 +1,46 @@
+From 5b97469a55872a30a0d53a1279a8ae8b1c68b52c Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd at arndb.de>
+Date: Mon, 4 Jul 2016 15:12:28 +0200
+Subject: thermal: qcom: tsens-8916: mark PM functions __maybe_unused
+
+The newly added tsens-8916 driver produces warnings when CONFIG_PM
+is disabled:
+
+drivers/thermal/qcom/tsens.c:53:12: error: 'tsens_resume' defined but not used [-Werror=unused-function]
+ static int tsens_resume(struct device *dev)
+            ^~~~~~~~~~~~
+drivers/thermal/qcom/tsens.c:43:12: error: 'tsens_suspend' defined but not used [-Werror=unused-function]
+ static int tsens_suspend(struct device *dev)
+            ^~~~~~~~~~~~~
+
+This marks both functions __maybe_unused to let the compiler
+know that they might be used in other configurations, without
+adding ugly #ifdef logic.
+
+Signed-off-by: Arnd Bergmann <arnd at arndb.de>
+Reviewed-by: Rajendra Nayak <rnayak at codeaurora.org>
+Signed-off-by: Zhang Rui <rui.zhang at intel.com>
+---
+ drivers/thermal/qcom/tsens.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -40,7 +40,7 @@ static int tsens_get_trend(void *data, l
+ 	return -ENOTSUPP;
+ }
+ 
+-static int tsens_suspend(struct device *dev)
++static int  __maybe_unused tsens_suspend(struct device *dev)
+ {
+ 	struct tsens_device *tmdev = dev_get_drvdata(dev);
+ 
+@@ -50,7 +50,7 @@ static int tsens_suspend(struct device *
+ 	return 0;
+ }
+ 
+-static int tsens_resume(struct device *dev)
++static int __maybe_unused tsens_resume(struct device *dev)
+ {
+ 	struct tsens_device *tmdev = dev_get_drvdata(dev);
+ 
diff --git a/target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch b/target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch
new file mode 100644
index 0000000..1ca7326
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch
@@ -0,0 +1,143 @@
+From e498b4984db82b4ba3ceea7dba813222a31e9c2e Mon Sep 17 00:00:00 2001
+From: Laxman Dewangan <ldewangan at nvidia.com>
+Date: Wed, 9 Mar 2016 18:40:06 +0530
+Subject: thermal: of-thermal: Add devm version of
+ thermal_zone_of_sensor_register
+
+Add resource managed version of thermal_zone_of_sensor_register() and
+thermal_zone_of_sensor_unregister().
+
+This helps in reducing the code size in error path, remove of
+driver remove callbacks and making proper sequence for deallocations.
+
+Signed-off-by: Laxman Dewangan <ldewangan at nvidia.com>
+Signed-off-by: Eduardo Valentin <edubezval at gmail.com>
+---
+ drivers/thermal/of-thermal.c | 81 ++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/thermal.h      | 18 ++++++++++
+ 2 files changed, 99 insertions(+)
+
+--- a/drivers/thermal/of-thermal.c
++++ b/drivers/thermal/of-thermal.c
+@@ -559,6 +559,87 @@ void thermal_zone_of_sensor_unregister(s
+ }
+ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
+ 
++static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
++{
++	thermal_zone_of_sensor_unregister(dev,
++					  *(struct thermal_zone_device **)res);
++}
++
++static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
++					     void *data)
++{
++	struct thermal_zone_device **r = res;
++
++	if (WARN_ON(!r || !*r))
++		return 0;
++
++	return *r == data;
++}
++
++/**
++ * devm_thermal_zone_of_sensor_register - Resource managed version of
++ *				thermal_zone_of_sensor_register()
++ * @dev: a valid struct device pointer of a sensor device. Must contain
++ *       a valid .of_node, for the sensor node.
++ * @sensor_id: a sensor identifier, in case the sensor IP has more
++ *	       than one sensors
++ * @data: a private pointer (owned by the caller) that will be passed
++ *	  back, when a temperature reading is needed.
++ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
++ *
++ * Refer thermal_zone_of_sensor_register() for more details.
++ *
++ * Return: On success returns a valid struct thermal_zone_device,
++ * otherwise, it returns a corresponding ERR_PTR(). Caller must
++ * check the return value with help of IS_ERR() helper.
++ * Registered hermal_zone_device device will automatically be
++ * released when device is unbounded.
++ */
++struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
++	struct device *dev, int sensor_id,
++	void *data, const struct thermal_zone_of_device_ops *ops)
++{
++	struct thermal_zone_device **ptr, *tzd;
++
++	ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
++			   GFP_KERNEL);
++	if (!ptr)
++		return ERR_PTR(-ENOMEM);
++
++	tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
++	if (IS_ERR(tzd)) {
++		devres_free(ptr);
++		return tzd;
++	}
++
++	*ptr = tzd;
++	devres_add(dev, ptr);
++
++	return tzd;
++}
++EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
++
++/**
++ * devm_thermal_zone_of_sensor_unregister - Resource managed version of
++ *				thermal_zone_of_sensor_unregister().
++ * @dev: Device for which which resource was allocated.
++ * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
++ *
++ * This function removes the sensor callbacks and private data from the
++ * thermal zone device registered with devm_thermal_zone_of_sensor_register()
++ * API. It will also silent the zone by remove the .get_temp() and .get_trend()
++ * thermal zone device callbacks.
++ * Normally this function will not need to be called and the resource
++ * management code will ensure that the resource is freed.
++ */
++void devm_thermal_zone_of_sensor_unregister(struct device *dev,
++					    struct thermal_zone_device *tzd)
++{
++	WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
++			       devm_thermal_zone_of_sensor_match, tzd));
++}
++EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
++
+ /***   functions parsing device tree nodes   ***/
+ 
+ /**
+--- a/include/linux/thermal.h
++++ b/include/linux/thermal.h
+@@ -364,6 +364,11 @@ thermal_zone_of_sensor_register(struct d
+ 				const struct thermal_zone_of_device_ops *ops);
+ void thermal_zone_of_sensor_unregister(struct device *dev,
+ 				       struct thermal_zone_device *tz);
++struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
++		struct device *dev, int id, void *data,
++		const struct thermal_zone_of_device_ops *ops);
++void devm_thermal_zone_of_sensor_unregister(struct device *dev,
++					    struct thermal_zone_device *tz);
+ #else
+ static inline struct thermal_zone_device *
+ thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
+@@ -378,6 +383,19 @@ void thermal_zone_of_sensor_unregister(s
+ {
+ }
+ 
++static inline struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
++		struct device *dev, int id, void *data,
++		const struct thermal_zone_of_device_ops *ops)
++{
++	return ERR_PTR(-ENODEV);
++}
++
++static inline
++void devm_thermal_zone_of_sensor_unregister(struct device *dev,
++					    struct thermal_zone_device *tz)
++{
++}
++
+ #endif
+ 
+ #if IS_ENABLED(CONFIG_THERMAL)
diff --git a/target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch b/target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch
new file mode 100644
index 0000000..3fbe5f5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch
@@ -0,0 +1,101 @@
+From 4a7069a32c99a81950de035535b0a064dcceaeba Mon Sep 17 00:00:00 2001
+From: Rajendra Nayak <rnayak at codeaurora.org>
+Date: Thu, 5 May 2016 14:21:42 +0530
+Subject: [PATCH] thermal: core: export apis to get slope and offset
+
+Add apis for platform thermal drivers to query for slope and offset
+attributes, which might be needed for temperature calculations.
+
+Signed-off-by: Rajendra Nayak <rnayak at codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval at gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang at intel.com>
+---
+ Documentation/thermal/sysfs-api.txt | 12 ++++++++++++
+ drivers/thermal/thermal_core.c      | 30 ++++++++++++++++++++++++++++++
+ include/linux/thermal.h             |  8 ++++++++
+ 3 files changed, 50 insertions(+)
+
+--- a/Documentation/thermal/sysfs-api.txt
++++ b/Documentation/thermal/sysfs-api.txt
+@@ -72,6 +72,18 @@ temperature) and throttle appropriate de
+     It deletes the corresponding entry form /sys/class/thermal folder and
+     unbind all the thermal cooling devices it uses.
+ 
++1.1.7 int thermal_zone_get_slope(struct thermal_zone_device *tz)
++
++	This interface is used to read the slope attribute value
++	for the thermal zone device, which might be useful for platform
++	drivers for temperature calculations.
++
++1.1.8 int thermal_zone_get_offset(struct thermal_zone_device *tz)
++
++	This interface is used to read the offset attribute value
++	for the thermal zone device, which might be useful for platform
++	drivers for temperature calculations.
++
+ 1.2 thermal cooling device interface
+ 1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
+ 		void *devdata, struct thermal_cooling_device_ops *)
+--- a/drivers/thermal/thermal_core.c
++++ b/drivers/thermal/thermal_core.c
+@@ -2061,6 +2061,36 @@ exit:
+ }
+ EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
+ 
++/**
++ * thermal_zone_get_slope - return the slope attribute of the thermal zone
++ * @tz: thermal zone device with the slope attribute
++ *
++ * Return: If the thermal zone device has a slope attribute, return it, else
++ * return 1.
++ */
++int thermal_zone_get_slope(struct thermal_zone_device *tz)
++{
++	if (tz && tz->tzp)
++		return tz->tzp->slope;
++	return 1;
++}
++EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
++
++/**
++ * thermal_zone_get_offset - return the offset attribute of the thermal zone
++ * @tz: thermal zone device with the offset attribute
++ *
++ * Return: If the thermal zone device has a offset attribute, return it, else
++ * return 0.
++ */
++int thermal_zone_get_offset(struct thermal_zone_device *tz)
++{
++	if (tz && tz->tzp)
++		return tz->tzp->offset;
++	return 0;
++}
++EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
++
+ #ifdef CONFIG_NET
+ static const struct genl_multicast_group thermal_event_mcgrps[] = {
+ 	{ .name = THERMAL_GENL_MCAST_GROUP_NAME, },
+--- a/include/linux/thermal.h
++++ b/include/linux/thermal.h
+@@ -432,6 +432,8 @@ thermal_of_cooling_device_register(struc
+ void thermal_cooling_device_unregister(struct thermal_cooling_device *);
+ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
+ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
++int thermal_zone_get_slope(struct thermal_zone_device *tz);
++int thermal_zone_get_offset(struct thermal_zone_device *tz);
+ 
+ int get_tz_trend(struct thermal_zone_device *, int);
+ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
+@@ -489,6 +491,12 @@ static inline struct thermal_zone_device
+ static inline int thermal_zone_get_temp(
+ 		struct thermal_zone_device *tz, int *temp)
+ { return -ENODEV; }
++static inline int thermal_zone_get_slope(
++		struct thermal_zone_device *tz)
++{ return -ENODEV; }
++static inline int thermal_zone_get_offset(
++		struct thermal_zone_device *tz)
++{ return -ENODEV; }
+ static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
+ { return -ENODEV; }
+ static inline struct thermal_instance *
diff --git a/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch b/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch
new file mode 100644
index 0000000..13415f5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch
@@ -0,0 +1,42 @@
+From 313a72ff983cc2e00ac4dcb791d40ebf2f9d5718 Mon Sep 17 00:00:00 2001
+From: Srinivas Kandagatla <srinivas.kandagatla at linaro.org>
+Date: Tue, 17 Nov 2015 09:12:41 +0000
+Subject: nvmem: core: return error for non word aligned access
+
+nvmem providers have restrictions on register strides, so return error
+when users attempt to read/write buffers with sizes which are less
+than word size.
+
+Without this patch the userspace would continue to try as it does not
+get any error from the nvmem core, resulting in a hang or endless loop
+in userspace.
+
+Reported-by: Ariel D'Alessandro <ariel at vanguardiasur.com.ar>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla at linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -70,6 +70,9 @@ static ssize_t bin_attr_nvmem_read(struc
+ 	if (pos >= nvmem->size)
+ 		return 0;
+ 
++	if (count < nvmem->word_size)
++		return -EINVAL;
++
+ 	if (pos + count > nvmem->size)
+ 		count = nvmem->size - pos;
+ 
+@@ -95,6 +98,9 @@ static ssize_t bin_attr_nvmem_write(stru
+ 	if (pos >= nvmem->size)
+ 		return 0;
+ 
++	if (count < nvmem->word_size)
++		return -EINVAL;
++
+ 	if (pos + count > nvmem->size)
+ 		count = nvmem->size - pos;
+ 
diff --git a/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch b/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch
new file mode 100644
index 0000000..1f9473b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch
@@ -0,0 +1,34 @@
+From dfdf141429f0895b63c882facc42c86f225033cb Mon Sep 17 00:00:00 2001
+From: Rasmus Villemoes <linux at rasmusvillemoes.dk>
+Date: Mon, 8 Feb 2016 22:04:29 +0100
+Subject: nvmem: core: fix error path in nvmem_add_cells()
+
+The current code fails to nvmem_cell_drop(cells[0]) - even worse, if
+the loop above fails already at i==0, we'll enter an essentially
+infinite loop doing nvmem_cell_drop on cells[-1], cells[-2], ... which
+is unlikely to end well.
+
+Also, we're not freeing the temporary backing array cells on the error
+path.
+
+Signed-off-by: Rasmus Villemoes <linux at rasmusvillemoes.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -294,9 +294,11 @@ static int nvmem_add_cells(struct nvmem_
+ 
+ 	return 0;
+ err:
+-	while (--i)
++	while (i--)
+ 		nvmem_cell_drop(cells[i]);
+ 
++	kfree(cells);
++
+ 	return rval;
+ }
+ 
diff --git a/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch b/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch
new file mode 100644
index 0000000..77136ea
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch
@@ -0,0 +1,101 @@
+From 811b0d6538b9f26f3eb0f90fe4e6118f2480ec6f Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <andrew at lunn.ch>
+Date: Fri, 26 Feb 2016 20:59:18 +0100
+Subject: nvmem: Add flag to export NVMEM to root only
+
+Legacy AT24, AT25 EEPROMs are exported in sys so that only root can
+read the contents. The EEPROMs may contain sensitive information. Add
+a flag so the provide can indicate that NVMEM should also restrict
+access to root only.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+Acked-by: Srinivas Kandagatla <srinivas.kandagatla at linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/nvmem/core.c           | 57 ++++++++++++++++++++++++++++++++++++++++--
+ include/linux/nvmem-provider.h |  1 +
+ 2 files changed, 56 insertions(+), 2 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -161,6 +161,53 @@ static const struct attribute_group *nvm
+ 	NULL,
+ };
+ 
++/* default read/write permissions, root only */
++static struct bin_attribute bin_attr_rw_root_nvmem = {
++	.attr	= {
++		.name	= "nvmem",
++		.mode	= S_IWUSR | S_IRUSR,
++	},
++	.read	= bin_attr_nvmem_read,
++	.write	= bin_attr_nvmem_write,
++};
++
++static struct bin_attribute *nvmem_bin_rw_root_attributes[] = {
++	&bin_attr_rw_root_nvmem,
++	NULL,
++};
++
++static const struct attribute_group nvmem_bin_rw_root_group = {
++	.bin_attrs	= nvmem_bin_rw_root_attributes,
++};
++
++static const struct attribute_group *nvmem_rw_root_dev_groups[] = {
++	&nvmem_bin_rw_root_group,
++	NULL,
++};
++
++/* read only permission, root only */
++static struct bin_attribute bin_attr_ro_root_nvmem = {
++	.attr	= {
++		.name	= "nvmem",
++		.mode	= S_IRUSR,
++	},
++	.read	= bin_attr_nvmem_read,
++};
++
++static struct bin_attribute *nvmem_bin_ro_root_attributes[] = {
++	&bin_attr_ro_root_nvmem,
++	NULL,
++};
++
++static const struct attribute_group nvmem_bin_ro_root_group = {
++	.bin_attrs	= nvmem_bin_ro_root_attributes,
++};
++
++static const struct attribute_group *nvmem_ro_root_dev_groups[] = {
++	&nvmem_bin_ro_root_group,
++	NULL,
++};
++
+ static void nvmem_release(struct device *dev)
+ {
+ 	struct nvmem_device *nvmem = to_nvmem_device(dev);
+@@ -355,8 +402,14 @@ struct nvmem_device *nvmem_register(cons
+ 	nvmem->read_only = of_property_read_bool(np, "read-only") |
+ 			   config->read_only;
+ 
+-	nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups :
+-					       nvmem_rw_dev_groups;
++	if (config->root_only)
++		nvmem->dev.groups = nvmem->read_only ?
++			nvmem_ro_root_dev_groups :
++			nvmem_rw_root_dev_groups;
++	else
++		nvmem->dev.groups = nvmem->read_only ?
++			nvmem_ro_dev_groups :
++			nvmem_rw_dev_groups;
+ 
+ 	device_initialize(&nvmem->dev);
+ 
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -23,6 +23,7 @@ struct nvmem_config {
+ 	const struct nvmem_cell_info	*cells;
+ 	int			ncells;
+ 	bool			read_only;
++	bool			root_only;
+ };
+ 
+ #if IS_ENABLED(CONFIG_NVMEM)
diff --git a/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch b/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch
new file mode 100644
index 0000000..6344d0e
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch
@@ -0,0 +1,181 @@
+From b6c217ab9be6895384cf0b284ace84ad79e5c53b Mon Sep 17 00:00:00 2001
+From: Andrew Lunn <andrew at lunn.ch>
+Date: Fri, 26 Feb 2016 20:59:19 +0100
+Subject: nvmem: Add backwards compatibility support for older EEPROM drivers.
+
+Older drivers made an 'eeprom' file available in the /sys device
+directory. Have the NVMEM core provide this to retain backwards
+compatibility.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+Acked-by: Srinivas Kandagatla <srinivas.kandagatla at linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
+---
+ drivers/nvmem/core.c           | 84 ++++++++++++++++++++++++++++++++++++++----
+ include/linux/nvmem-provider.h |  4 +-
+ 2 files changed, 79 insertions(+), 9 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -38,8 +38,13 @@ struct nvmem_device {
+ 	int			users;
+ 	size_t			size;
+ 	bool			read_only;
++	int			flags;
++	struct bin_attribute	eeprom;
++	struct device		*base_dev;
+ };
+ 
++#define FLAG_COMPAT		BIT(0)
++
+ struct nvmem_cell {
+ 	const char		*name;
+ 	int			offset;
+@@ -56,16 +61,26 @@ static DEFINE_IDA(nvmem_ida);
+ static LIST_HEAD(nvmem_cells);
+ static DEFINE_MUTEX(nvmem_cells_mutex);
+ 
++#ifdef CONFIG_DEBUG_LOCK_ALLOC
++static struct lock_class_key eeprom_lock_key;
++#endif
++
+ #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
+ 
+ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
+ 				    struct bin_attribute *attr,
+ 				    char *buf, loff_t pos, size_t count)
+ {
+-	struct device *dev = container_of(kobj, struct device, kobj);
+-	struct nvmem_device *nvmem = to_nvmem_device(dev);
++	struct device *dev;
++	struct nvmem_device *nvmem;
+ 	int rc;
+ 
++	if (attr->private)
++		dev = attr->private;
++	else
++		dev = container_of(kobj, struct device, kobj);
++	nvmem = to_nvmem_device(dev);
++
+ 	/* Stop the user from reading */
+ 	if (pos >= nvmem->size)
+ 		return 0;
+@@ -90,10 +105,16 @@ static ssize_t bin_attr_nvmem_write(stru
+ 				     struct bin_attribute *attr,
+ 				     char *buf, loff_t pos, size_t count)
+ {
+-	struct device *dev = container_of(kobj, struct device, kobj);
+-	struct nvmem_device *nvmem = to_nvmem_device(dev);
++	struct device *dev;
++	struct nvmem_device *nvmem;
+ 	int rc;
+ 
++	if (attr->private)
++		dev = attr->private;
++	else
++		dev = container_of(kobj, struct device, kobj);
++	nvmem = to_nvmem_device(dev);
++
+ 	/* Stop the user from writing */
+ 	if (pos >= nvmem->size)
+ 		return 0;
+@@ -349,6 +370,43 @@ err:
+ 	return rval;
+ }
+ 
++/*
++ * nvmem_setup_compat() - Create an additional binary entry in
++ * drivers sys directory, to be backwards compatible with the older
++ * drivers/misc/eeprom drivers.
++ */
++static int nvmem_setup_compat(struct nvmem_device *nvmem,
++			      const struct nvmem_config *config)
++{
++	int rval;
++
++	if (!config->base_dev)
++		return -EINVAL;
++
++	if (nvmem->read_only)
++		nvmem->eeprom = bin_attr_ro_root_nvmem;
++	else
++		nvmem->eeprom = bin_attr_rw_root_nvmem;
++	nvmem->eeprom.attr.name = "eeprom";
++	nvmem->eeprom.size = nvmem->size;
++#ifdef CONFIG_DEBUG_LOCK_ALLOC
++	nvmem->eeprom.attr.key = &eeprom_lock_key;
++#endif
++	nvmem->eeprom.private = &nvmem->dev;
++	nvmem->base_dev = config->base_dev;
++
++	rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom);
++	if (rval) {
++		dev_err(&nvmem->dev,
++			"Failed to create eeprom binary file %d\n", rval);
++		return rval;
++	}
++
++	nvmem->flags |= FLAG_COMPAT;
++
++	return 0;
++}
++
+ /**
+  * nvmem_register() - Register a nvmem device for given nvmem_config.
+  * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+@@ -416,16 +474,23 @@ struct nvmem_device *nvmem_register(cons
+ 	dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+ 
+ 	rval = device_add(&nvmem->dev);
+-	if (rval) {
+-		ida_simple_remove(&nvmem_ida, nvmem->id);
+-		kfree(nvmem);
+-		return ERR_PTR(rval);
++	if (rval)
++		goto out;
++
++	if (config->compat) {
++		rval = nvmem_setup_compat(nvmem, config);
++		if (rval)
++			goto out;
+ 	}
+ 
+ 	if (config->cells)
+ 		nvmem_add_cells(nvmem, config);
+ 
+ 	return nvmem;
++out:
++	ida_simple_remove(&nvmem_ida, nvmem->id);
++	kfree(nvmem);
++	return ERR_PTR(rval);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_register);
+ 
+@@ -445,6 +510,9 @@ int nvmem_unregister(struct nvmem_device
+ 	}
+ 	mutex_unlock(&nvmem_mutex);
+ 
++	if (nvmem->flags & FLAG_COMPAT)
++		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
++
+ 	nvmem_device_remove_all_cells(nvmem);
+ 	device_del(&nvmem->dev);
+ 
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -24,6 +24,9 @@ struct nvmem_config {
+ 	int			ncells;
+ 	bool			read_only;
+ 	bool			root_only;
++	/* To be only used by old driver/misc/eeprom drivers */
++	bool			compat;
++	struct device		*base_dev;
+ };
+ 
+ #if IS_ENABLED(CONFIG_NVMEM)
+@@ -44,5 +47,4 @@ static inline int nvmem_unregister(struc
+ }
+ 
+ #endif /* CONFIG_NVMEM */
+-
+ #endif  /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch b/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch
new file mode 100644
index 0000000..0eae3e7
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch
@@ -0,0 +1,38 @@
+From 856371ca1561ca9b3280cc323ff296c7c5e1fa93 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] 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 | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -3109,6 +3109,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;
+ 
+@@ -3138,6 +3139,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;
+ }
+ 
diff --git a/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch b/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch
new file mode 100644
index 0000000..b2564a5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch
@@ -0,0 +1,150 @@
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -31,6 +31,9 @@
+ 			clock-latency = <100000>;
+ 			cpu-supply = <&smb208_s2a>;
+ 			voltage-tolerance = <5>;
++			cooling-min-state = <0>;
++			cooling-max-state = <10>;
++			#cooling-cells = <2>;
+ 			cpu-idle-states = <&CPU_SPC>;
+ 		};
+ 
+@@ -46,6 +49,9 @@
+ 			clock-names = "cpu", "l2";
+ 			clock-latency = <100000>;
+ 			cpu-supply = <&smb208_s2b>;
++			cooling-min-state = <0>;
++			cooling-max-state = <10>;
++			#cooling-cells = <2>;
+ 			cpu-idle-states = <&CPU_SPC>;
+ 		};
+ 
+@@ -70,6 +76,92 @@
+ 		};
+ 	};
+ 
++	thermal-zones {
++		cpu-thermal0 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 5>;
++			coefficients = <1132 0>;
++
++			trips {
++				cpu_alert0: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit0: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++
++		cpu-thermal1 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 6>;
++			coefficients = <1132 0>;
++
++			trips {
++				cpu_alert1: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit1: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++
++		cpu-thermal2 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 7>;
++			coefficients = <1199 0>;
++
++			trips {
++				cpu_alert2: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit2: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++
++		cpu-thermal3 {
++			polling-delay-passive = <250>;
++			polling-delay = <1000>;
++
++			thermal-sensors = <&gcc 8>;
++			coefficients = <1132 0>;
++
++			trips {
++				cpu_alert3: trip0 {
++					temperature = <75000>;
++					hysteresis = <2000>;
++					type = "passive";
++				};
++				cpu_crit3: trip1 {
++					temperature = <110000>;
++					hysteresis = <2000>;
++					type = "critical";
++				};
++			};
++		};
++	};
++
+ 	cpu-pmu {
+ 		compatible = "qcom,krait-pmu";
+ 		interrupts = <1 10 0x304>;
+@@ -172,6 +264,21 @@
+ 			reg-names = "lpass-lpaif";
+ 		};
+ 
++		qfprom: qfprom at 700000 {
++			compatible = "qcom,qfprom", "syscon";
++			reg = <0x00700000 0x1000>;
++			#address-cells = <1>;
++			#size-cells = <1>;
++			ranges;
++
++			tsens_calib: calib {
++				reg = <0x400 0x10>;
++			};
++			tsens_backup: backup_calib {
++				reg = <0x410 0x10>;
++			};
++		};
++
+ 		rpm at 108000 {
+ 			compatible = "qcom,rpm-ipq8064";
+ 			reg = <0x108000 0x1000>;
+@@ -499,8 +606,12 @@
+ 		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>;
++			#thermal-sensor-cells = <1>;
+ 		};
+ 
+ 		tcsr: syscon at 1a400000 {
diff --git a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
index aa12121..f6f3572 100644
--- a/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
+++ b/target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -121,7 +121,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  };
 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
-@@ -793,6 +793,92 @@
+@@ -904,6 +904,92 @@
  
  			status = "disabled";
  		};



More information about the lede-commits mailing list