aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch')
-rw-r--r--target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch627
1 files changed, 627 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch b/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch
new file mode 100644
index 0000000000..b427550cd8
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.14/0063-1-ipq806x-tsens-driver.patch
@@ -0,0 +1,627 @@
+From 3302e1e1a3cfa4e67fda2a61d6f0c42205d40932 Mon Sep 17 00:00:00 2001
+From: Rajith Cherian <rajith@codeaurora.org>
+Date: Tue, 14 Feb 2017 18:30:43 +0530
+Subject: [PATCH] ipq8064: tsens: Base tsens driver for IPQ8064
+
+Add TSENS driver template to support IPQ8064.
+This is a base file copied from tsens-8960.c
+
+Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7
+Signed-off-by: Rajith Cherian <rajith@codeaurora.org>
+
+ipq8064: tsens: TSENS driver support for IPQ8064
+
+Support for IPQ8064 tsens driver. The driver works
+with the thermal framework. The driver overrides the
+following fucntionalities:
+
+1. Get current temperature.
+2. Get/Set trip temperatures.
+3. Enabled/Disable trip points.
+4. ISR for threshold generated interrupt.
+5. Notify userspace when trip points are hit.
+
+Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918
+Signed-off-by: Rajith Cherian <rajith@codeaurora.org>
+---
+ .../devicetree/bindings/thermal/qcom-tsens.txt | 1 +
+ drivers/thermal/qcom/Makefile | 3 +-
+ drivers/thermal/qcom/tsens-ipq8064.c | 551 +++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c | 3 +
+ drivers/thermal/qcom/tsens.h | 2 +-
+ 5 files changed, 558 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/thermal/qcom/tsens-ipq8064.c
+
+--- 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.
+--- 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
+--- /dev/null
++++ b/drivers/thermal/qcom/tsens-ipq8064.c
+@@ -0,0 +1,551 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/bitops.h>
++#include <linux/regmap.h>
++#include <linux/thermal.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include "tsens.h"
++
++#define CAL_MDEGC 30000
++
++#define CONFIG_ADDR 0x3640
++/* CONFIG_ADDR bitmasks */
++#define CONFIG 0x9b
++#define CONFIG_MASK 0xf
++#define CONFIG_SHIFT 0
++
++#define STATUS_CNTL_8064 0x3660
++#define CNTL_ADDR 0x3620
++/* CNTL_ADDR bitmasks */
++#define EN BIT(0)
++#define SW_RST BIT(1)
++#define SENSOR0_EN BIT(3)
++#define SLP_CLK_ENA BIT(26)
++#define MEASURE_PERIOD 1
++#define SENSOR0_SHIFT 3
++
++/* INT_STATUS_ADDR bitmasks */
++#define MIN_STATUS_MASK BIT(0)
++#define LOWER_STATUS_CLR BIT(1)
++#define UPPER_STATUS_CLR BIT(2)
++#define MAX_STATUS_MASK BIT(3)
++
++#define THRESHOLD_ADDR 0x3624
++/* THRESHOLD_ADDR bitmasks */
++#define THRESHOLD_MAX_CODE 0x20000
++#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 1
++
++/* Trips: from very hot to very cold */
++enum tsens_trip_type {
++ TSENS_TRIP_STAGE3 = 0,
++ TSENS_TRIP_STAGE2,
++ TSENS_TRIP_STAGE1,
++ TSENS_TRIP_STAGE0,
++ TSENS_TRIP_NUM,
++};
++
++u32 tsens_8064_slope[] = {
++ 1176, 1176, 1154, 1176,
++ 1111, 1132, 1132, 1199,
++ 1132, 1199, 1132
++ };
++
++/* Temperature on y axis and ADC-code on x-axis */
++static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s)
++{
++ int degcbeforefactor, degc;
++
++ degcbeforefactor = (adc_code * s->slope) + s->offset;
++
++ if (degcbeforefactor == 0)
++ degc = degcbeforefactor;
++ else if (degcbeforefactor > 0)
++ degc = (degcbeforefactor + TSENS_FACTOR/2)
++ / TSENS_FACTOR;
++ else
++ degc = (degcbeforefactor - TSENS_FACTOR/2)
++ / TSENS_FACTOR;
++
++ return degc;
++}
++
++static int degC_to_code(int degC, const struct tsens_sensor *s)
++{
++ int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2))
++ / s->slope;
++
++ if (code > THRESHOLD_MAX_CODE)
++ code = THRESHOLD_MAX_CODE;
++ else if (code < THRESHOLD_MIN_CODE)
++ code = THRESHOLD_MIN_CODE;
++ return code;
++}
++
++static int suspend_ipq8064(struct tsens_device *tmdev)
++{
++ int ret;
++ unsigned int mask;
++ struct regmap *map = tmdev->map;
++
++ ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
++ if (ret)
++ return ret;
++
++ ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
++ if (ret)
++ return ret;
++
++ mask = SLP_CLK_ENA | EN;
++
++ ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int resume_ipq8064(struct tsens_device *tmdev)
++{
++ int ret;
++ struct regmap *map = tmdev->map;
++
++ ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
++ if (ret)
++ return ret;
++
++ ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
++ if (ret)
++ return ret;
++
++ ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
++ if (ret)
++ return ret;
++
++ ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static void notify_uspace_tsens_fn(struct work_struct *work)
++{
++ struct tsens_sensor *s = container_of(work, struct tsens_sensor,
++ notify_work);
++
++ sysfs_notify(&s->tzd->device.kobj, NULL, "type");
++}
++
++static void tsens_scheduler_fn(struct work_struct *work)
++{
++ struct tsens_device *tmdev = container_of(work, struct tsens_device,
++ tsens_work);
++ unsigned int threshold, threshold_low, code, reg, sensor, mask;
++ unsigned int sensor_addr;
++ bool upper_th_x, lower_th_x;
++ int adc_code, ret;
++
++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg);
++ if (ret)
++ return;
++ reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
++ ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg);
++ if (ret)
++ return;
++
++ mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
++ ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold);
++ if (ret)
++ return;
++ threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK)
++ >> THRESHOLD_LOWER_LIMIT_SHIFT;
++ threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK)
++ >> THRESHOLD_UPPER_LIMIT_SHIFT;
++
++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg);
++ if (ret)
++ return;
++
++ ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor);
++ if (ret)
++ return;
++ sensor &= (uint32_t) TSENS_8064_SENSORS_EN;
++ sensor >>= SENSOR0_SHIFT;
++
++ /* Constraint: There is only 1 interrupt control register for all
++ * 11 temperature sensor. So monitoring more than 1 sensor based
++ * on interrupts will yield inconsistent result. To overcome this
++ * issue we will monitor only sensor 0 which is the master sensor.
++ */
++
++ /* Skip if the sensor is disabled */
++ if (sensor & 1) {
++ ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code);
++ if (ret)
++ return;
++ upper_th_x = code >= threshold;
++ lower_th_x = code <= threshold_low;
++ if (upper_th_x)
++ mask |= UPPER_STATUS_CLR;
++ if (lower_th_x)
++ mask |= LOWER_STATUS_CLR;
++ if (upper_th_x || lower_th_x) {
++ /* Notify user space */
++ schedule_work(&tmdev->sensor[0].notify_work);
++ regmap_read(tmdev->map, sensor_addr, &adc_code);
++ pr_debug("Trigger (%d degrees) for sensor %d\n",
++ code_to_degC(adc_code, &tmdev->sensor[0]), 0);
++ }
++ }
++ regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask);
++
++ /* force memory to sync */
++ mb();
++}
++
++static irqreturn_t tsens_isr(int irq, void *data)
++{
++ struct tsens_device *tmdev = data;
++
++ schedule_work(&tmdev->tsens_work);
++ return IRQ_HANDLED;
++}
++
++static void hw_init(struct tsens_device *tmdev)
++{
++ int ret;
++ unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
++ unsigned int reg_status_cntl = 0;
++
++ regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
++
++ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18)
++ | (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT);
++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++ regmap_read(tmdev->map, STATUS_CNTL_8064, &reg_status_cntl);
++ reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR
++ | MIN_STATUS_MASK | MAX_STATUS_MASK;
++ regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl);
++ reg_cntl |= TSENS_EN;
++ regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++
++ regmap_read(tmdev->map, CONFIG_ADDR, &reg_cfg);
++ reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT);
++ regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg);
++
++ reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT)
++ | (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT)
++ | (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT)
++ | (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
++
++ regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr);
++
++ ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr,
++ IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev);
++ if (ret < 0) {
++ pr_err("%s: request_irq FAIL: %d\n", __func__, ret);
++ return;
++ }
++
++ INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn);
++}
++
++static int init_ipq8064(struct tsens_device *tmdev)
++{
++ int ret, i;
++ u32 reg_cntl, offset = 0;
++
++ init_common(tmdev);
++ if (!tmdev->map)
++ return -ENODEV;
++
++ /*
++ * The status registers for each sensor are discontiguous
++ * because some SoCs have 5 sensors while others have more
++ * but the control registers stay in the same place, i.e
++ * directly after the first 5 status registers.
++ */
++ for (i = 0; i < tmdev->num_sensors; i++) {
++ if (i >= TSENS_8064_SEQ_SENSORS)
++ offset = TSENS_8064_S4_S5_OFFSET;
++
++ tmdev->sensor[i].status = S0_STATUS_ADDR + offset
++ + (i << STATUS_ADDR_OFFSET);
++ tmdev->sensor[i].slope = tsens_8064_slope[i];
++ INIT_WORK(&tmdev->sensor[i].notify_work,
++ notify_uspace_tsens_fn);
++ }
++
++ reg_cntl = SW_RST;
++ ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
++ if (ret)
++ return ret;
++
++ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
++ reg_cntl &= ~SW_RST;
++ ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
++ CONFIG_MASK, CONFIG);
++
++ reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++ if (ret)
++ return ret;
++
++ reg_cntl |= EN;
++ ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int calibrate_ipq8064(struct tsens_device *tmdev)
++{
++ int i;
++ char *data, *data_backup;
++
++ ssize_t num_read = tmdev->num_sensors;
++ struct tsens_sensor *s = tmdev->sensor;
++
++ data = qfprom_read(tmdev->dev, "calib");
++ if (IS_ERR(data)) {
++ pr_err("Calibration not found.\n");
++ return PTR_ERR(data);
++ }
++
++ data_backup = qfprom_read(tmdev->dev, "calib_backup");
++ if (IS_ERR(data_backup)) {
++ pr_err("Backup calibration not found.\n");
++ return PTR_ERR(data_backup);
++ }
++
++ for (i = 0; i < num_read; i++) {
++ s[i].calib_data = readb_relaxed(data + i);
++ s[i].calib_data_backup = readb_relaxed(data_backup + i);
++
++ if (s[i].calib_data_backup)
++ s[i].calib_data = s[i].calib_data_backup;
++ if (!s[i].calib_data) {
++ pr_err("QFPROM TSENS calibration data not present\n");
++ return -ENODEV;
++ }
++ s[i].slope = tsens_8064_slope[i];
++ s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope);
++ }
++
++ hw_init(tmdev);
++
++ return 0;
++}
++
++static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp)
++{
++ int ret;
++ u32 code, trdy;
++ const struct tsens_sensor *s = &tmdev->sensor[id];
++ unsigned long timeout;
++
++ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
++ do {
++ ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
++ if (ret)
++ return ret;
++ if (!(trdy & TRDY_MASK))
++ continue;
++ ret = regmap_read(tmdev->map, s->status, &code);
++ if (ret)
++ return ret;
++ *temp = code_to_degC(code, s);
++ return 0;
++ } while (time_before(jiffies, timeout));
++
++ return -ETIMEDOUT;
++}
++
++static int set_trip_temp_ipq8064(void *data, int trip, int temp)
++{
++ unsigned int reg_th, reg_cntl;
++ int ret, code, code_chk, hi_code, lo_code;
++ const struct tsens_sensor *s = data;
++ struct tsens_device *tmdev = s->tmdev;
++
++ code_chk = code = degC_to_code(temp, s);
++
++ if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE)
++ return -EINVAL;
++
++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg_cntl);
++ if (ret)
++ return ret;
++
++ ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &reg_th);
++ if (ret)
++ return ret;
++
++ hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK)
++ >> THRESHOLD_UPPER_LIMIT_SHIFT;
++ lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK)
++ >> THRESHOLD_LOWER_LIMIT_SHIFT;
++
++ switch (trip) {
++ case TSENS_TRIP_STAGE3:
++ code <<= THRESHOLD_MAX_LIMIT_SHIFT;
++ reg_th &= ~THRESHOLD_MAX_LIMIT_MASK;
++ break;
++ case TSENS_TRIP_STAGE2:
++ if (code_chk <= lo_code)
++ return -EINVAL;
++ code <<= THRESHOLD_UPPER_LIMIT_SHIFT;
++ reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK;
++ break;
++ case TSENS_TRIP_STAGE1:
++ if (code_chk >= hi_code)
++ return -EINVAL;
++ code <<= THRESHOLD_LOWER_LIMIT_SHIFT;
++ reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK;
++ break;
++ case TSENS_TRIP_STAGE0:
++ code <<= THRESHOLD_MIN_LIMIT_SHIFT;
++ reg_th &= ~THRESHOLD_MIN_LIMIT_MASK;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int set_trip_activate_ipq8064(void *data, int trip,
++ enum thermal_trip_activation_mode mode)
++{
++ unsigned int reg_cntl, mask, val;
++ const struct tsens_sensor *s = data;
++ struct tsens_device *tmdev = s->tmdev;
++ int ret;
++
++ if (!tmdev || trip < 0)
++ return -EINVAL;
++
++ ret = regmap_read(tmdev->map, STATUS_CNTL_8064, &reg_cntl);
++ if (ret)
++ return ret;
++
++ switch (trip) {
++ case TSENS_TRIP_STAGE3:
++ mask = MAX_STATUS_MASK;
++ break;
++ case TSENS_TRIP_STAGE2:
++ mask = UPPER_STATUS_CLR;
++ break;
++ case TSENS_TRIP_STAGE1:
++ mask = LOWER_STATUS_CLR;
++ break;
++ case TSENS_TRIP_STAGE0:
++ mask = MIN_STATUS_MASK;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
++ val = reg_cntl | mask;
++ else
++ val = reg_cntl & ~mask;
++
++ ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val);
++ if (ret)
++ return ret;
++
++ /* force memory to sync */
++ mb();
++ return 0;
++}
++
++const struct tsens_ops ops_ipq8064 = {
++ .init = init_ipq8064,
++ .calibrate = calibrate_ipq8064,
++ .get_temp = get_temp_ipq8064,
++ .suspend = suspend_ipq8064,
++ .resume = resume_ipq8064,
++ .set_trip_temp = set_trip_temp_ipq8064,
++ .set_trip_activate = set_trip_activate_ipq8064,
++};
++
++const struct tsens_data data_ipq8064 = {
++ .num_sensors = 11,
++ .ops = &ops_ipq8064,
++};
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -72,6 +72,9 @@ static const struct of_device_id tsens_t
+ }, {
+ .compatible = "qcom,msm8996-tsens",
+ .data = &data_8996,
++ }, {
++ .compatible = "qcom,ipq8064-tsens",
++ .data = &data_ipq8064,
+ },
+ {}
+ };
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -89,6 +89,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, data_8960, data_8996;
++extern const struct tsens_data data_8916, data_8974, data_8960, data_8996, data_ipq8064;
+
+ #endif /* __QCOM_TSENS_H__ */