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