aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordissent1 <be.dissent+github@gmail.com>2016-11-05 16:15:33 +0300
committerJohn Crispin <john@phrozen.org>2016-12-01 15:47:43 +0100
commitfef6a96d9e9e001a28b6a673c56ed79029457b5d (patch)
tree808f89926a7a7a2b3b29564e2070592ef983d948
parent2b71f958b1d2022bd0c4dcaa458f5ba1b955f934 (diff)
downloadupstream-fef6a96d9e9e001a28b6a673c56ed79029457b5d.tar.gz
upstream-fef6a96d9e9e001a28b6a673c56ed79029457b5d.tar.bz2
upstream-fef6a96d9e9e001a28b6a673c56ed79029457b5d.zip
ipq806x: add thermal sensor driver
Allows to check cpu temperature. Huge thanks to @hnyman for valuable assistance! Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
-rw-r--r--target/linux/ipq806x/config-4.41
-rw-r--r--target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi109
-rw-r--r--target/linux/ipq806x/patches-4.4/015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch536
-rw-r--r--target/linux/ipq806x/patches-4.4/015-2-thermal-qcom-tsens-8916-Add-support-for-8916-family-of-SoCs.patch165
-rw-r--r--target/linux/ipq806x/patches-4.4/015-3-thermal-qcom-tsens-8974-Add-support-for-8974-family-of-SoCs.patch293
-rw-r--r--target/linux/ipq806x/patches-4.4/015-4-thermal-qcom-tsens-8960-Add-support-for-8960-family-of-SoCs.patch364
-rw-r--r--target/linux/ipq806x/patches-4.4/015-8-qcom-tsens-8916-mark-PM-functions-__maybe_unused.patch46
-rw-r--r--target/linux/ipq806x/patches-4.4/016-2-thermal-of-thermal-Add-devm-version-of.patch143
-rw-r--r--target/linux/ipq806x/patches-4.4/017-09-thermal-core-export-apis-to-get-slope-and-offset.patch101
-rw-r--r--target/linux/ipq806x/patches-4.4/019-1-nvmem-core-return-error-for-non-word-aligned-access.patch42
-rw-r--r--target/linux/ipq806x/patches-4.4/019-2-nvmem-core-fix-error-path-in-nvmem_add_cells.patch34
-rw-r--r--target/linux/ipq806x/patches-4.4/019-3-nvmem-Add-flag-to-export-NVMEM-to-root-only.patch101
-rw-r--r--target/linux/ipq806x/patches-4.4/019-4-nvmem-Add-backwards-compatibility-support-for-older-EEPROM-drivers.patch181
-rw-r--r--target/linux/ipq806x/patches-4.4/309-clk-gcc-add-tsens-child-node.patch38
-rw-r--r--target/linux/ipq806x/patches-4.4/310-add-necessary-thermal-data.patch150
-rw-r--r--target/linux/ipq806x/patches-4.4/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch2
16 files changed, 2293 insertions, 13 deletions
diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4
index 56a2254489..cdb20ebb05 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 e0ba99a41c..614610c016 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@108000 {
@@ -687,9 +779,12 @@
gcc: clock-controller@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@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 0000000000..ef7e2f4152
--- /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@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@codeaurora.org>
+Reviewed-by: Lina Iyer <lina.iyer@linaro.org>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang@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@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 0000000000..d07619645c
--- /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@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@codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang@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 0000000000..671f461057
--- /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@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@codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang@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 0000000000..05490cd97d
--- /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@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@codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang@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 0000000000..39d2173fe9
--- /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@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@arndb.de>
+Reviewed-by: Rajendra Nayak <rnayak@codeaurora.org>
+Signed-off-by: Zhang Rui <rui.zhang@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 0000000000..1ca7326832
--- /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@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@nvidia.com>
+Signed-off-by: Eduardo Valentin <edubezval@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 0000000000..3fbe5f521a
--- /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@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@codeaurora.org>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Zhang Rui <rui.zhang@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 0000000000..13415f51d0
--- /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@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@vanguardiasur.com.ar>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@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 0000000000..1f9473bafd
--- /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@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@rasmusvillemoes.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@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 0000000000..77136eab72
--- /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@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@lunn.ch>
+Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@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 0000000000..6344d0ed03
--- /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@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@lunn.ch>
+Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@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 0000000000..0eae3e7bfd
--- /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@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@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 0000000000..b2564a5407
--- /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@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@108000 {
+ compatible = "qcom,rpm-ipq8064";
+ reg = <0x108000 0x1000>;
+@@ -499,8 +606,12 @@
+ gcc: clock-controller@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@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 aa12121dff..f6f357253f 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@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";
};