Use improved tsens patch proposed upstream.
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+++ /dev/null
-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>
----
-
---- a/drivers/thermal/qcom/Makefile
-+++ b/drivers/thermal/qcom/Makefile
-@@ -2,5 +2,5 @@
- obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
-
- qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \
-- tsens-8960.o tsens-v2.o tsens-v1.o
-+ tsens-8960.o tsens-v2.o tsens-v1.o tsens-ipq8064.o
- obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.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_priv *priv)
-+{
-+ int ret;
-+ unsigned int mask;
-+ struct regmap *map = priv->tm_map;
-+
-+ ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_read(map, CNTL_ADDR, &priv->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_priv *priv)
-+{
-+ int ret;
-+ struct regmap *map = priv->tm_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, priv->ctx.threshold);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_write(map, CNTL_ADDR, priv->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_priv *priv = container_of(work, struct tsens_priv,
-+ 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(priv->tm_map, STATUS_CNTL_8064, ®);
-+ if (ret)
-+ return;
-+ reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
-+ ret = regmap_write(priv->tm_map, STATUS_CNTL_8064, reg);
-+ if (ret)
-+ return;
-+
-+ mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
-+ ret = regmap_read(priv->tm_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(priv->tm_map, STATUS_CNTL_8064, ®);
-+ if (ret)
-+ return;
-+
-+ ret = regmap_read(priv->tm_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(priv->tm_map, priv->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(&priv->sensor[0].notify_work);
-+ regmap_read(priv->tm_map, sensor_addr, &adc_code);
-+ pr_debug("Trigger (%d degrees) for sensor %d\n",
-+ code_to_degC(adc_code, &priv->sensor[0]), 0);
-+ }
-+ }
-+ regmap_write(priv->tm_map, STATUS_CNTL_8064, reg & mask);
-+
-+ /* force memory to sync */
-+ mb();
-+}
-+
-+static irqreturn_t tsens_isr(int irq, void *data)
-+{
-+ struct tsens_priv *priv = data;
-+
-+ schedule_work(&priv->tsens_work);
-+ return IRQ_HANDLED;
-+}
-+
-+static void hw_init(struct tsens_priv *priv)
-+{
-+ int ret;
-+ unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
-+ unsigned int reg_status_cntl = 0;
-+
-+ regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl);
-+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
-+
-+ reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18)
-+ | (((1 << priv->num_sensors) - 1) << SENSOR0_SHIFT);
-+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
-+ regmap_read(priv->tm_map, STATUS_CNTL_8064, ®_status_cntl);
-+ reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR
-+ | MIN_STATUS_MASK | MAX_STATUS_MASK;
-+ regmap_write(priv->tm_map, STATUS_CNTL_8064, reg_status_cntl);
-+ reg_cntl |= TSENS_EN;
-+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
-+
-+ regmap_read(priv->tm_map, CONFIG_ADDR, ®_cfg);
-+ reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT);
-+ regmap_write(priv->tm_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(priv->tm_map, THRESHOLD_ADDR, reg_thr);
-+
-+ ret = devm_request_irq(priv->dev, priv->tsens_irq, tsens_isr,
-+ IRQF_TRIGGER_RISING, "tsens_interrupt", priv);
-+ if (ret < 0) {
-+ pr_err("%s: request_irq FAIL: %d\n", __func__, ret);
-+ return;
-+ }
-+
-+ INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
-+}
-+
-+static int init_ipq8064(struct tsens_priv *priv)
-+{
-+ int ret, i;
-+ u32 reg_cntl, offset = 0;
-+
-+ init_common(priv);
-+ if (!priv->tm_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 < priv->num_sensors; i++) {
-+ if (i >= TSENS_8064_SEQ_SENSORS)
-+ offset = TSENS_8064_S4_S5_OFFSET;
-+
-+ priv->sensor[i].status = S0_STATUS_ADDR + offset
-+ + (i << STATUS_ADDR_OFFSET);
-+ priv->sensor[i].slope = tsens_8064_slope[i];
-+ INIT_WORK(&priv->sensor[i].notify_work,
-+ notify_uspace_tsens_fn);
-+ }
-+
-+ reg_cntl = SW_RST;
-+ ret = regmap_update_bits(priv->tm_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(priv->tm_map, CONFIG_ADDR,
-+ CONFIG_MASK, CONFIG);
-+
-+ reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT;
-+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
-+ if (ret)
-+ return ret;
-+
-+ reg_cntl |= EN;
-+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int calibrate_ipq8064(struct tsens_priv *priv)
-+{
-+ int i;
-+ char *data, *data_backup;
-+
-+ ssize_t num_read = priv->num_sensors;
-+ struct tsens_sensor *s = priv->sensor;
-+
-+ data = qfprom_read(priv->dev, "calib");
-+ if (IS_ERR(data)) {
-+ pr_err("Calibration not found.\n");
-+ return PTR_ERR(data);
-+ }
-+
-+ data_backup = qfprom_read(priv->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(priv);
-+
-+ return 0;
-+}
-+
-+static int get_temp_ipq8064(struct tsens_priv *priv, int id, int *temp)
-+{
-+ int ret;
-+ u32 code, trdy;
-+ const struct tsens_sensor *s = &priv->sensor[id];
-+ unsigned long timeout;
-+
-+ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
-+ do {
-+ ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy);
-+ if (ret)
-+ return ret;
-+ if (!(trdy & TRDY_MASK))
-+ continue;
-+ ret = regmap_read(priv->tm_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_priv *priv = s->priv;
-+
-+ code_chk = code = degC_to_code(temp, s);
-+
-+ if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE)
-+ return -EINVAL;
-+
-+ ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®_cntl);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_read(priv->tm_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(priv->tm_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_priv *priv = s->priv;
-+ int ret;
-+
-+ if (!priv || trip < 0)
-+ return -EINVAL;
-+
-+ ret = regmap_read(priv->tm_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(priv->tm_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_plat_data data_ipq8064 = {
-+ .num_sensors = 11,
-+ .ops = &ops_ipq8064,
-+};
---- a/drivers/thermal/qcom/tsens.c
-+++ b/drivers/thermal/qcom/tsens.c
-@@ -69,8 +69,11 @@ static const struct of_device_id tsens_t
- }, {
- .compatible = "qcom,tsens-v2",
- .data = &data_tsens_v2,
-+ }, {
-+ .compatible = "qcom,ipq8064-tsens",
-+ .data = &data_ipq8064,
- },
-- {}
-+ {}
- };
- MODULE_DEVICE_TABLE(of, tsens_table);
-
---- a/drivers/thermal/qcom/tsens.h
-+++ b/drivers/thermal/qcom/tsens.h
-@@ -324,7 +324,7 @@ extern const struct tsens_plat_data data
- extern const struct tsens_plat_data data_8916, data_8974;
-
- /* TSENS v1 targets */
--extern const struct tsens_plat_data data_tsens_v1;
-+extern const struct tsens_plat_data data_tsens_v1, data_ipq8064;
-
- /* TSENS v2 targets */
- extern const struct tsens_plat_data data_8996, data_tsens_v2;
+++ /dev/null
-From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001
-From: Rajith Cherian <rajith@codeaurora.org>
-Date: Wed, 1 Feb 2017 19:00:26 +0530
-Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts
-
-Provide support for adding configurable high and
-configurable low trip temperatures. An interrupts is
-also triggerred when these trip points are hit. The
-interrupts can be activated or deactivated from sysfs.
-This functionality is made available only if
-CONFIG_THERMAL_WRITABLE_TRIPS is defined.
-
-Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934
-Signed-off-by: Rajith Cherian <rajith@codeaurora.org>
----
- .../devicetree/bindings/thermal/qcom-tsens.txt | 4 ++
- drivers/thermal/of-thermal.c | 63 ++++++++++++++++++----
- drivers/thermal/qcom/tsens.c | 43 ++++++++++++---
- drivers/thermal/qcom/tsens.h | 11 ++++
- drivers/thermal/thermal_core.c | 44 ++++++++++++++-
- include/linux/thermal.h | 14 +++++
- 6 files changed, 162 insertions(+), 17 deletions(-)
-
---- a/drivers/thermal/of-thermal.c
-+++ b/drivers/thermal/of-thermal.c
-@@ -91,7 +91,7 @@ static int of_thermal_get_temp(struct th
- {
- 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);
-@@ -102,7 +102,8 @@ static int of_thermal_set_trips(struct t
- {
- 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);
-@@ -188,6 +189,9 @@ static int of_thermal_set_emul_temp(stru
- {
- struct __thermal_zone *data = tz->devdata;
-
-+ if (data->mode == THERMAL_DEVICE_DISABLED)
-+ return -EINVAL;
-+
- return data->ops->set_emul_temp(data->sensor_data, temp);
- }
-
-@@ -196,7 +200,7 @@ static int of_thermal_get_trend(struct t
- {
- 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);
-@@ -297,7 +301,9 @@ static int of_thermal_set_mode(struct th
- 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;
- }
-@@ -307,7 +313,8 @@ static int of_thermal_get_trip_type(stru
- {
- 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;
-@@ -315,12 +322,39 @@ static int of_thermal_get_trip_type(stru
- 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;
-@@ -333,7 +367,8 @@ static int of_thermal_set_trip_temp(stru
- {
- 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) {
-@@ -355,7 +390,8 @@ static int of_thermal_get_trip_hyst(stru
- {
- 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;
-@@ -368,7 +404,8 @@ static int of_thermal_set_trip_hyst(stru
- {
- 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) */
-@@ -443,6 +480,9 @@ thermal_zone_of_add_sensor(struct device
- 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;
-@@ -762,7 +802,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",
- };
-
- /**
---- a/drivers/thermal/qcom/tsens.c
-+++ b/drivers/thermal/qcom/tsens.c
-@@ -22,7 +22,7 @@ static int tsens_get_temp(void *data, in
-
- static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
- {
-- const struct tsens_sensor *s = data;
-+ struct tsens_sensor *s = data;
- struct tsens_priv *priv = s->priv;
-
- if (priv->ops->get_trend)
-@@ -31,9 +31,10 @@ static int tsens_get_trend(void *data, i
- return -ENOTSUPP;
- }
-
--static int __maybe_unused tsens_suspend(struct device *dev)
-+static int __maybe_unused tsens_suspend(void *data)
- {
-- struct tsens_priv *priv = dev_get_drvdata(dev);
-+ struct tsens_sensor *s = data;
-+ struct tsens_priv *priv = s->priv;
-
- if (priv->ops && priv->ops->suspend)
- return priv->ops->suspend(priv);
-@@ -41,9 +42,10 @@ static int __maybe_unused tsens_suspend
- return 0;
- }
-
--static int __maybe_unused tsens_resume(struct device *dev)
-+static int __maybe_unused tsens_resume(void *data)
- {
-- struct tsens_priv *priv = dev_get_drvdata(dev);
-+ struct tsens_sensor *s = data;
-+ struct tsens_priv *priv = s->priv;
-
- if (priv->ops && priv->ops->resume)
- return priv->ops->resume(priv);
-@@ -51,6 +53,30 @@ static int __maybe_unused tsens_resume(s
- return 0;
- }
-
-+static int __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp)
-+{
-+ struct tsens_sensor *s = data;
-+ struct tsens_priv *priv = s->priv;
-+
-+ if (priv->ops && priv->ops->set_trip_temp)
-+ return priv->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_priv *priv = s->priv;
-+
-+ if (priv->ops && priv->ops->set_trip_activate)
-+ return priv->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[] = {
-@@ -80,6 +106,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_priv *priv)
-@@ -123,7 +151,7 @@ static int tsens_probe(struct platform_d
- if (id)
- data = id->data;
- else
-- data = &data_8960;
-+ return -EINVAL;
-
- num_sensors = data->num_sensors;
-
-@@ -144,6 +172,9 @@ static int tsens_probe(struct platform_d
- priv->dev = dev;
- priv->num_sensors = num_sensors;
- priv->ops = data->ops;
-+
-+ priv->tsens_irq = platform_get_irq(pdev, 0);
-+
- for (i = 0; i < priv->num_sensors; i++) {
- if (data->hw_ids)
- priv->sensor[i].hw_id = data->hw_ids[i];
---- a/drivers/thermal/qcom/tsens.h
-+++ b/drivers/thermal/qcom/tsens.h
-@@ -40,9 +40,12 @@ enum tsens_ver {
- struct tsens_sensor {
- struct tsens_priv *priv;
- struct thermal_zone_device *tzd;
-+ struct work_struct notify_work;
- int offset;
- unsigned int id;
- unsigned int hw_id;
-+ int calib_data;
-+ int calib_data_backup;
- int slope;
- u32 status;
- };
-@@ -57,6 +60,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 */
-@@ -69,6 +75,9 @@ struct tsens_ops {
- int (*suspend)(struct tsens_priv *priv);
- int (*resume)(struct tsens_priv *priv);
- int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend);
-+ int (*set_trip_temp)(void *data, int trip, int temp);
-+ int (*set_trip_activate)(void *data, int trip,
-+ enum thermal_trip_activation_mode mode);
- };
-
- #define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \
-@@ -300,6 +309,7 @@ struct tsens_context {
- struct tsens_priv {
- struct device *dev;
- u32 num_sensors;
-+ u32 tsens_irq;
- struct regmap *tm_map;
- struct regmap *srot_map;
- u32 tm_offset;
-@@ -308,6 +318,7 @@ struct tsens_priv {
- const struct tsens_features *feat;
- const struct reg_field *fields;
- const struct tsens_ops *ops;
-+ struct work_struct tsens_work;
- struct tsens_sensor sensor[0];
- };
-
---- a/drivers/thermal/thermal_sysfs.c
-+++ b/drivers/thermal/thermal_sysfs.c
-@@ -113,12 +113,48 @@ trip_point_type_show(struct device *dev,
- 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)
- {
-@@ -559,6 +595,12 @@ static int create_trip_attrs(struct ther
- tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
- attrs[indx] = &tz->trip_type_attrs[indx].attr.attr;
-
-+ 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;
-+ }
-+
- /* create trip temp attribute */
- snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
- "trip_point_%d_temp", indx);
---- a/include/linux/thermal.h
-+++ b/include/linux/thermal.h
-@@ -63,11 +63,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 {
-@@ -105,6 +113,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 *);
-@@ -349,6 +359,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 *);
-@@ -356,6 +368,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);
- };
-
- /**
+++ /dev/null
---- a/drivers/thermal/qcom/tsens-ipq8064.c
-+++ b/drivers/thermal/qcom/tsens-ipq8064.c
-@@ -18,6 +18,7 @@
- #include <linux/regmap.h>
- #include <linux/thermal.h>
- #include <linux/nvmem-consumer.h>
-+#include <linux/of_platform.h>
- #include <linux/io.h>
- #include <linux/interrupt.h>
- #include "tsens.h"
-@@ -320,15 +321,42 @@ static void hw_init(struct tsens_priv *p
- INIT_WORK(&priv->tsens_work, tsens_scheduler_fn);
- }
-
-+static const struct regmap_config tsens_config = {
-+ .name = "tm",
-+ .reg_bits = 32,
-+ .val_bits = 32,
-+ .reg_stride = 4,
-+};
-+
- static int init_ipq8064(struct tsens_priv *priv)
- {
-- int ret, i;
-+ struct device *dev = priv->dev;
- u32 reg_cntl, offset = 0;
-+ struct resource *res;
-+ resource_size_t size;
-+ void __iomem *base;
-+ int ret, i;
-+ struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
-+
-+ if (!op)
-+ return -EINVAL;
-
-- init_common(priv);
-- if (!priv->tm_map)
-- return -ENODEV;
-+ /* old DTs where SROT and TM were in a contiguous 2K block */
-+ priv->tm_offset = 0x1000;
-
-+ res = platform_get_resource(op, IORESOURCE_MEM, 0);
-+ size = resource_size(res);
-+ base = devm_ioremap(&op->dev, res->start, size);
-+ if (IS_ERR(base)) {
-+ ret = PTR_ERR(base);
-+ goto err_put_device;
-+ }
-+
-+ priv->tm_map = devm_regmap_init_mmio(dev, base, &tsens_config);
-+ if (IS_ERR(priv->tm_map)) {
-+ ret = PTR_ERR(priv->tm_map);
-+ goto err_put_device;
-+ }
- /*
- * The status registers for each sensor are discontiguous
- * because some SoCs have 5 sensors while others have more
-@@ -367,6 +395,10 @@ static int init_ipq8064(struct tsens_pri
- return ret;
-
- return 0;
-+
-+err_put_device:
-+ put_device(&op->dev);
-+ return ret;
- }
-
- static int calibrate_ipq8064(struct tsens_priv *priv)
+++ /dev/null
---- a/drivers/thermal/qcom/tsens-ipq8064.c
-+++ b/drivers/thermal/qcom/tsens-ipq8064.c
-@@ -13,10 +13,12 @@
- */
-
- #include <linux/platform_device.h>
-+#include <linux/err.h>
- #include <linux/delay.h>
- #include <linux/bitops.h>
- #include <linux/regmap.h>
- #include <linux/thermal.h>
-+#include <linux/slab.h>
- #include <linux/nvmem-consumer.h>
- #include <linux/of_platform.h>
- #include <linux/io.h>
-@@ -211,9 +213,8 @@ static void tsens_scheduler_fn(struct wo
- struct tsens_priv *priv = container_of(work, struct tsens_priv,
- 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;
-+ int ret;
-
- ret = regmap_read(priv->tm_map, STATUS_CNTL_8064, ®);
- if (ret)
-@@ -262,9 +263,8 @@ static void tsens_scheduler_fn(struct wo
- if (upper_th_x || lower_th_x) {
- /* Notify user space */
- schedule_work(&priv->sensor[0].notify_work);
-- regmap_read(priv->tm_map, sensor_addr, &adc_code);
- pr_debug("Trigger (%d degrees) for sensor %d\n",
-- code_to_degC(adc_code, &priv->sensor[0]), 0);
-+ code_to_degC(code, &priv->sensor[0]), 0);
- }
- }
- regmap_write(priv->tm_map, STATUS_CNTL_8064, reg & mask);
-@@ -404,40 +404,55 @@ err_put_device:
- static int calibrate_ipq8064(struct tsens_priv *priv)
- {
- int i;
-- char *data, *data_backup;
--
-+ int ret = 0;
-+ u8 *data, *data_backup;
-+ struct device *dev = priv->dev;
- ssize_t num_read = priv->num_sensors;
- struct tsens_sensor *s = priv->sensor;
-
-- data = qfprom_read(priv->dev, "calib");
-+ data = qfprom_read(dev, "calib");
- if (IS_ERR(data)) {
-- pr_err("Calibration not found.\n");
-- return PTR_ERR(data);
-+ ret = PTR_ERR(data);
-+ if (ret != -EPROBE_DEFER)
-+ dev_err(dev, "Calibration not found.");
-+ goto exit;
- }
-
-- data_backup = qfprom_read(priv->dev, "calib_backup");
-+ data_backup = qfprom_read(dev, "calib_backup");
- if (IS_ERR(data_backup)) {
-- pr_err("Backup calibration not found.\n");
-- return PTR_ERR(data_backup);
-+ ret = PTR_ERR(data_backup);
-+ if (ret != -EPROBE_DEFER)
-+ dev_err(dev, "Backup Calibration not found.");
-+ goto free_data;
- }
-
- 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) {
-+ s[i].calib_data_backup = readb_relaxed(data_backup + i);
-+
-+ if (!s[i].calib_data_backup) {
-+ dev_err(dev, "QFPROM TSENS calibration data not present");
-+ ret = -ENODEV;
-+ goto free_backup;
-+ }
-
-- 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(priv);
-
-- return 0;
-+free_backup:
-+ kfree(data_backup);
-+free_data:
-+ kfree(data);
-+exit:
-+ return ret;
- }
-
- static int get_temp_ipq8064(struct tsens_priv *priv, int id, int *temp)
--- /dev/null
+From 5c7d1181056feef0b58fb2f556f55e170ba5b479 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 25 Jul 2020 19:14:59 +0200
+Subject: [PATCH 01/10] drivers: thermal: tsens: Add VER_0 tsens version
+
+VER_0 is used to describe device based on tsens version before v0.1.
+These device are devices based on msm8960 for example apq8064 or
+ipq806x.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org>
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/thermal/qcom/tsens.c | 150 ++++++++++++++++++++++++++++-------
+ drivers/thermal/qcom/tsens.h | 4 +-
+ 2 files changed, 124 insertions(+), 30 deletions(-)
+
+diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
+index d8ce3a687b80..9a7e991d4bd2 100644
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -12,6 +12,7 @@
+ #include <linux/of.h>
+ #include <linux/of_address.h>
+ #include <linux/of_platform.h>
++#include <linux/mfd/syscon.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm.h>
+ #include <linux/regmap.h>
+@@ -515,6 +516,15 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
+ dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
+ hw_id, __func__, temp);
+ }
++
++ if (tsens_version(priv) < VER_0_1) {
++ /* 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.
++ */
++ break;
++ }
+ }
+
+ return IRQ_HANDLED;
+@@ -530,6 +540,13 @@ static int tsens_set_trips(void *_sensor, int low, int high)
+ int high_val, low_val, cl_high, cl_low;
+ u32 hw_id = s->hw_id;
+
++ if (tsens_version(priv) < VER_0_1) {
++ /* Pre v0.1 IP had a single register for each type of interrupt
++ * and thresholds
++ */
++ hw_id = 0;
++ }
++
+ dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
+ hw_id, __func__, low, high);
+
+@@ -584,18 +601,21 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
+ u32 valid;
+ int ret;
+
+- ret = regmap_field_read(priv->rf[valid_idx], &valid);
+- if (ret)
+- return ret;
+- while (!valid) {
+- /* Valid bit is 0 for 6 AHB clock cycles.
+- * At 19.2MHz, 1 AHB clock is ~60ns.
+- * We should enter this loop very, very rarely.
+- */
+- ndelay(400);
++ /* VER_0 doesn't have VALID bit */
++ if (tsens_version(priv) >= VER_0_1) {
+ ret = regmap_field_read(priv->rf[valid_idx], &valid);
+ if (ret)
+ return ret;
++ while (!valid) {
++ /* Valid bit is 0 for 6 AHB clock cycles.
++ * At 19.2MHz, 1 AHB clock is ~60ns.
++ * We should enter this loop very, very rarely.
++ */
++ ndelay(400);
++ ret = regmap_field_read(priv->rf[valid_idx], &valid);
++ if (ret)
++ return ret;
++ }
+ }
+
+ /* Valid bit is set, OK to read the temperature */
+@@ -608,15 +628,29 @@ int get_temp_common(const struct tsens_sensor *s, int *temp)
+ {
+ struct tsens_priv *priv = s->priv;
+ int hw_id = s->hw_id;
+- int last_temp = 0, ret;
++ int last_temp = 0, ret, trdy;
++ unsigned long timeout;
+
+- ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
+- if (ret)
+- return ret;
++ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
++ do {
++ if (tsens_version(priv) == VER_0) {
++ ret = regmap_field_read(priv->rf[TRDY], &trdy);
++ if (ret)
++ return ret;
++ if (!trdy)
++ continue;
++ }
+
+- *temp = code_to_degc(last_temp, s) * 1000;
++ ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
++ if (ret)
++ return ret;
+
+- return 0;
++ *temp = code_to_degc(last_temp, s) * 1000;
++
++ return 0;
++ } while (time_before(jiffies, timeout));
++
++ return -ETIMEDOUT;
+ }
+
+ #ifdef CONFIG_DEBUG_FS
+@@ -738,19 +772,34 @@ int __init init_common(struct tsens_priv *priv)
+ priv->tm_offset = 0x1000;
+ }
+
+- res = platform_get_resource(op, IORESOURCE_MEM, 0);
+- tm_base = devm_ioremap_resource(dev, res);
+- if (IS_ERR(tm_base)) {
+- ret = PTR_ERR(tm_base);
+- goto err_put_device;
++ if (tsens_version(priv) >= VER_0_1) {
++ res = platform_get_resource(op, IORESOURCE_MEM, 0);
++ tm_base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(tm_base)) {
++ ret = PTR_ERR(tm_base);
++ goto err_put_device;
++ }
++
++ priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
++ } else { /* VER_0 share the same gcc regs using a syscon */
++ struct device *parent = priv->dev->parent;
++
++ if (parent)
++ priv->tm_map = syscon_node_to_regmap(parent->of_node);
+ }
+
+- priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
+- if (IS_ERR(priv->tm_map)) {
+- ret = PTR_ERR(priv->tm_map);
++ if (IS_ERR_OR_NULL(priv->tm_map)) {
++ if (!priv->tm_map)
++ ret = -ENODEV;
++ else
++ ret = PTR_ERR(priv->tm_map);
+ goto err_put_device;
+ }
+
++ /* VER_0 have only tm_map */
++ if (!priv->srot_map)
++ priv->srot_map = priv->tm_map;
++
+ if (tsens_version(priv) > VER_0_1) {
+ for (i = VER_MAJOR; i <= VER_STEP; i++) {
+ priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
+@@ -769,6 +818,10 @@ int __init init_common(struct tsens_priv *priv)
+ ret = PTR_ERR(priv->rf[TSENS_EN]);
+ goto err_put_device;
+ }
++ /* in VER_0 TSENS need to be explicitly enabled */
++ if (tsens_version(priv) == VER_0)
++ regmap_field_write(priv->rf[TSENS_EN], 1);
++
+ ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
+ if (ret)
+ goto err_put_device;
+@@ -791,6 +844,19 @@ int __init init_common(struct tsens_priv *priv)
+ goto err_put_device;
+ }
+
++ priv->rf[TSENS_SW_RST] =
++ devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]);
++ if (IS_ERR(priv->rf[TSENS_SW_RST])) {
++ ret = PTR_ERR(priv->rf[TSENS_SW_RST]);
++ goto err_put_device;
++ }
++
++ priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]);
++ if (IS_ERR(priv->rf[TRDY])) {
++ ret = PTR_ERR(priv->rf[TRDY]);
++ goto err_put_device;
++ }
++
+ /* This loop might need changes if enum regfield_ids is reordered */
+ for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
+ for (i = 0; i < priv->feat->max_sensors; i++) {
+@@ -806,7 +872,7 @@ int __init init_common(struct tsens_priv *priv)
+ }
+ }
+
+- if (priv->feat->crit_int) {
++ if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) {
+ /* Loop might need changes if enum regfield_ids is reordered */
+ for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
+ for (i = 0; i < priv->feat->max_sensors; i++) {
+@@ -844,7 +910,11 @@ int __init init_common(struct tsens_priv *priv)
+ }
+
+ spin_lock_init(&priv->ul_lock);
+- tsens_enable_irq(priv);
++
++ /* VER_0 interrupt doesn't need to be enabled */
++ if (tsens_version(priv) >= VER_0_1)
++ tsens_enable_irq(priv);
++
+ tsens_debug_init(op);
+
+ err_put_device:
+@@ -943,10 +1013,19 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
+ if (irq == -ENXIO)
+ ret = 0;
+ } else {
+- ret = devm_request_threaded_irq(&pdev->dev, irq,
+- NULL, thread_fn,
+- IRQF_ONESHOT,
+- dev_name(&pdev->dev), priv);
++ /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */
++ if (tsens_version(priv) == VER_0)
++ ret = devm_request_threaded_irq(&pdev->dev, irq,
++ thread_fn, NULL,
++ IRQF_TRIGGER_RISING,
++ dev_name(&pdev->dev),
++ priv);
++ else
++ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
++ thread_fn, IRQF_ONESHOT,
++ dev_name(&pdev->dev),
++ priv);
++
+ if (ret)
+ dev_err(&pdev->dev, "%s: failed to get irq\n",
+ __func__);
+@@ -975,6 +1054,19 @@ static int tsens_register(struct tsens_priv *priv)
+ priv->ops->enable(priv, i);
+ }
+
++ /* VER_0 require to set MIN and MAX THRESH
++ * These 2 regs are set using the:
++ * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C
++ * - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C
++ */
++ if (tsens_version(priv) < VER_0_1) {
++ regmap_field_write(priv->rf[CRIT_THRESH_0],
++ tsens_mC_to_hw(priv->sensor, 120000));
++
++ regmap_field_write(priv->rf[CRIT_THRESH_1],
++ tsens_mC_to_hw(priv->sensor, 0));
++ }
++
+ ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
+ if (ret < 0)
+ return ret;
+diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
+index f40b625f897e..8e6c1fd3ccf5 100644
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -13,6 +13,7 @@
+ #define CAL_DEGC_PT2 120
+ #define SLOPE_FACTOR 1000
+ #define SLOPE_DEFAULT 3200
++#define TIMEOUT_US 100
+ #define THRESHOLD_MAX_ADC_CODE 0x3ff
+ #define THRESHOLD_MIN_ADC_CODE 0x0
+
+@@ -25,7 +26,8 @@ struct tsens_priv;
+
+ /* IP version numbers in ascending order */
+ enum tsens_ver {
+- VER_0_1 = 0,
++ VER_0 = 0,
++ VER_0_1,
+ VER_1_X,
+ VER_2_X,
+ };
+--
+2.30.2
+
--- /dev/null
+From efa0d50a6c5ec7619371dfe4d3e6ca54b73787d5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 25 Nov 2020 16:47:21 +0100
+Subject: [PATCH 02/10] drivers: thermal: tsens: Don't hardcode sensor slope
+
+Function compute_intercept_slope hardcode the sensor slope to
+SLOPE_DEFAULT. Change this and use the default value only if a slope is
+not defined. This is needed for tsens VER_0 that has a hardcoded slope
+table.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
+index 9a7e991d4bd2..38b9936def1a 100644
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -86,7 +86,8 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
+ "%s: sensor%d - data_point1:%#x data_point2:%#x\n",
+ __func__, i, p1[i], p2[i]);
+
+- priv->sensor[i].slope = SLOPE_DEFAULT;
++ if (!priv->sensor[i].slope)
++ priv->sensor[i].slope = SLOPE_DEFAULT;
+ if (mode == TWO_PT_CALIB) {
+ /*
+ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
+--
+2.30.2
+
--- /dev/null
+From 6bac2e2fa36c2d7c304768a689d8b73155b90aa2 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 25 Nov 2020 17:15:51 +0100
+Subject: [PATCH 03/10] drivers: thermal: tsens: Convert msm8960 to reg_field
+
+Convert msm9860 driver to reg_field to use the init_common
+function.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Acked-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens-8960.c | 80 ++++++++++++++++++++++++++++++-
+ 1 file changed, 79 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
+index 2a28a5af209e..3f4fc1ffe679 100644
+--- a/drivers/thermal/qcom/tsens-8960.c
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -51,11 +51,22 @@
+ #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
+
++#define S0_STATUS_OFF 0x3628
++#define S1_STATUS_OFF 0x362c
++#define S2_STATUS_OFF 0x3630
++#define S3_STATUS_OFF 0x3634
++#define S4_STATUS_OFF 0x3638
++#define S5_STATUS_OFF 0x3664 /* Sensors 5-10 found on apq8064/msm8960 */
++#define S6_STATUS_OFF 0x3668
++#define S7_STATUS_OFF 0x366c
++#define S8_STATUS_OFF 0x3670
++#define S9_STATUS_OFF 0x3674
++#define S10_STATUS_OFF 0x3678
++
+ static int suspend_8960(struct tsens_priv *priv)
+ {
+ int ret;
+@@ -269,6 +280,71 @@ static int get_temp_8960(const struct tsens_sensor *s, int *temp)
+ return -ETIMEDOUT;
+ }
+
++static struct tsens_features tsens_8960_feat = {
++ .ver_major = VER_0,
++ .crit_int = 0,
++ .adc = 1,
++ .srot_split = 0,
++ .max_sensors = 11,
++};
++
++static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = {
++ /* ----- SROT ------ */
++ /* No VERSION information */
++
++ /* CNTL */
++ [TSENS_EN] = REG_FIELD(CNTL_ADDR, 0, 0),
++ [TSENS_SW_RST] = REG_FIELD(CNTL_ADDR, 1, 1),
++ /* 8960 has 5 sensors, 8660 has 11, we only handle 5 */
++ [SENSOR_EN] = REG_FIELD(CNTL_ADDR, 3, 7),
++
++ /* ----- TM ------ */
++ /* INTERRUPT ENABLE */
++ /* NO INTERRUPT ENABLE */
++
++ /* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */
++ [LOW_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 0, 7),
++ [UP_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 8, 15),
++ /* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield
++ * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp
++ * MIN_THRESH_0 -> CRIT_THRESH_1
++ * MAX_THRESH_0 -> CRIT_THRESH_0
++ */
++ [CRIT_THRESH_1] = REG_FIELD(THRESHOLD_ADDR, 16, 23),
++ [CRIT_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 24, 31),
++
++ /* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */
++ /* 1 == clear, 0 == normal operation */
++ [LOW_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 9, 9),
++ [UP_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 10, 10),
++
++ /* NO CRITICAL INTERRUPT SUPPORT on 8960 */
++
++ /* Sn_STATUS */
++ [LAST_TEMP_0] = REG_FIELD(S0_STATUS_OFF, 0, 7),
++ [LAST_TEMP_1] = REG_FIELD(S1_STATUS_OFF, 0, 7),
++ [LAST_TEMP_2] = REG_FIELD(S2_STATUS_OFF, 0, 7),
++ [LAST_TEMP_3] = REG_FIELD(S3_STATUS_OFF, 0, 7),
++ [LAST_TEMP_4] = REG_FIELD(S4_STATUS_OFF, 0, 7),
++ [LAST_TEMP_5] = REG_FIELD(S5_STATUS_OFF, 0, 7),
++ [LAST_TEMP_6] = REG_FIELD(S6_STATUS_OFF, 0, 7),
++ [LAST_TEMP_7] = REG_FIELD(S7_STATUS_OFF, 0, 7),
++ [LAST_TEMP_8] = REG_FIELD(S8_STATUS_OFF, 0, 7),
++ [LAST_TEMP_9] = REG_FIELD(S9_STATUS_OFF, 0, 7),
++ [LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0, 7),
++
++ /* No VALID field on 8960 */
++ /* TSENS_INT_STATUS bits: 1 == threshold violated */
++ [MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0),
++ [LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1),
++ [UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2),
++ /* No CRITICAL field on 8960 */
++ [MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3),
++
++ /* TRDY: 1=ready, 0=in progress */
++ [TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7),
++};
++
+ static const struct tsens_ops ops_8960 = {
+ .init = init_8960,
+ .calibrate = calibrate_8960,
+@@ -282,4 +358,6 @@ static const struct tsens_ops ops_8960 = {
+ struct tsens_plat_data data_8960 = {
+ .num_sensors = 11,
+ .ops = &ops_8960,
++ .feat = &tsens_8960_feat,
++ .fields = tsens_8960_regfields,
+ };
+--
+2.30.2
+
--- /dev/null
+From c04f98a496929f75d75c65115d5717423c3d0634 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 25 Nov 2020 17:16:36 +0100
+Subject: [PATCH 04/10] drivers: thermal: tsens: Use init_common for msm8960
+
+Use init_common and drop custom init for msm8960.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens-8960.c | 52 +------------------------------
+ 1 file changed, 1 insertion(+), 51 deletions(-)
+
+diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
+index 3f4fc1ffe679..86585f439985 100644
+--- a/drivers/thermal/qcom/tsens-8960.c
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -173,56 +173,6 @@ static void disable_8960(struct tsens_priv *priv)
+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+ }
+
+-static int init_8960(struct tsens_priv *priv)
+-{
+- int ret, i;
+- u32 reg_cntl;
+-
+- priv->tm_map = dev_get_regmap(priv->dev, NULL);
+- if (!priv->tm_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 < priv->num_sensors; i++) {
+- if (i >= 5)
+- priv->sensor[i].status = S0_STATUS_ADDR + 40;
+- priv->sensor[i].status += i * 4;
+- }
+-
+- reg_cntl = SW_RST;
+- ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
+- if (ret)
+- return ret;
+-
+- if (priv->num_sensors > 1) {
+- reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
+- reg_cntl &= ~SW_RST;
+- ret = regmap_update_bits(priv->tm_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(priv->num_sensors - 1, 0) << SENSOR0_SHIFT;
+- ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+- if (ret)
+- return ret;
+-
+- reg_cntl |= EN;
+- ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
+- if (ret)
+- return ret;
+-
+- return 0;
+-}
+-
+ static int calibrate_8960(struct tsens_priv *priv)
+ {
+ int i;
+@@ -346,7 +296,7 @@ static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = {
+ };
+
+ static const struct tsens_ops ops_8960 = {
+- .init = init_8960,
++ .init = init_common,
+ .calibrate = calibrate_8960,
+ .get_temp = get_temp_8960,
+ .enable = enable_8960,
+--
+2.30.2
+
--- /dev/null
+From b3e8bd33b84a6b6c863bd1733bd15b5f1483b8ab Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 25 Nov 2020 17:06:55 +0100
+Subject: [PATCH 05/10] drivers: thermal: tsens: Fix bug in sensor enable for
+ msm8960
+
+Device based on tsens VER_0 contains a hardware bug that results in some
+problem with sensor enablement. Sensor id 6-11 can't be enabled
+selectively and all of them must be enabled in one step.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Acked-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens-8960.c | 23 ++++++++++++++++++++---
+ 1 file changed, 20 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
+index 86585f439985..95fcccafae14 100644
+--- a/drivers/thermal/qcom/tsens-8960.c
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -27,9 +27,9 @@
+ #define EN BIT(0)
+ #define SW_RST BIT(1)
+ #define SENSOR0_EN BIT(3)
++#define MEASURE_PERIOD BIT(18)
+ #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 */
+@@ -126,17 +126,34 @@ static int resume_8960(struct tsens_priv *priv)
+ static int enable_8960(struct tsens_priv *priv, int id)
+ {
+ int ret;
+- u32 reg, mask;
++ u32 reg, mask = BIT(id);
+
+ ret = regmap_read(priv->tm_map, CNTL_ADDR, ®);
+ if (ret)
+ return ret;
+
+- mask = BIT(id + SENSOR0_SHIFT);
++ /* HARDWARE BUG:
++ * On platforms with more than 6 sensors, all remaining sensors
++ * must be enabled together, otherwise undefined results are expected.
++ * (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver,
++ * all the sensors are enabled in one step hence this bug is not
++ * triggered.
++ */
++ if (id > 5)
++ mask = GENMASK(10, 6);
++
++ mask <<= SENSOR0_SHIFT;
++
++ /* Sensors already enabled. Skip. */
++ if ((reg & mask) == mask)
++ return 0;
++
+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
+ if (ret)
+ return ret;
+
++ reg |= MEASURE_PERIOD;
++
+ if (priv->num_sensors > 1)
+ reg |= mask | SLP_CLK_ENA | EN;
+ else
+--
+2.30.2
+
--- /dev/null
+From 1ff9f982051759e0387e8c7e793b49c48eae291d Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 25 Nov 2020 17:11:05 +0100
+Subject: [PATCH 06/10] drivers: thermal: tsens: Replace custom 8960 apis with
+ generic apis
+
+Rework calibrate function to use common function. Derive the offset from
+a missing hardcoded slope table and the data from the nvmem calib
+efuses.
+Drop custom get_temp function and use generic api.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Acked-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens-8960.c | 56 +++++++++----------------------
+ 1 file changed, 15 insertions(+), 41 deletions(-)
+
+diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
+index 95fcccafae14..9cc8a7dd23ae 100644
+--- a/drivers/thermal/qcom/tsens-8960.c
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -67,6 +67,13 @@
+ #define S9_STATUS_OFF 0x3674
+ #define S10_STATUS_OFF 0x3678
+
++/* Original slope - 200 to compensate mC to C inaccuracy */
++static u32 tsens_msm8960_slope[] = {
++ 976, 976, 954, 976,
++ 911, 932, 932, 999,
++ 932, 999, 932
++ };
++
+ static int suspend_8960(struct tsens_priv *priv)
+ {
+ int ret;
+@@ -194,9 +201,7 @@ static int calibrate_8960(struct tsens_priv *priv)
+ {
+ int i;
+ char *data;
+-
+- ssize_t num_read = priv->num_sensors;
+- struct tsens_sensor *s = priv->sensor;
++ u32 p1[11];
+
+ data = qfprom_read(priv->dev, "calib");
+ if (IS_ERR(data))
+@@ -204,49 +209,18 @@ static int calibrate_8960(struct tsens_priv *priv)
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+- for (i = 0; i < num_read; i++, s++)
+- s->offset = data[i];
++ for (i = 0; i < priv->num_sensors; i++) {
++ p1[i] = data[i];
++ priv->sensor[i].slope = tsens_msm8960_slope[i];
++ }
++
++ compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB);
+
+ kfree(data);
+
+ 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(const struct tsens_sensor *s, int *temp)
+-{
+- int ret;
+- u32 code, trdy;
+- struct tsens_priv *priv = s->priv;
+- unsigned long timeout;
+-
+- timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
+- do {
+- ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy);
+- if (ret)
+- return ret;
+- if (!(trdy & TRDY_MASK))
+- continue;
+- ret = regmap_read(priv->tm_map, s->status, &code);
+- if (ret)
+- return ret;
+- *temp = code_to_mdegC(code, s);
+- return 0;
+- } while (time_before(jiffies, timeout));
+-
+- return -ETIMEDOUT;
+-}
+-
+ static struct tsens_features tsens_8960_feat = {
+ .ver_major = VER_0,
+ .crit_int = 0,
+@@ -315,7 +289,7 @@ static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = {
+ static const struct tsens_ops ops_8960 = {
+ .init = init_common,
+ .calibrate = calibrate_8960,
+- .get_temp = get_temp_8960,
++ .get_temp = get_temp_common,
+ .enable = enable_8960,
+ .disable = disable_8960,
+ .suspend = suspend_8960,
+--
+2.30.2
+
--- /dev/null
+From 5716a61239c6ac9ceb137e825e93c3aea06c4634 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 19 Mar 2021 00:48:23 +0100
+Subject: [PATCH 07/10] drivers: thermal: tsens: Drop unused define for msm8960
+
+Drop unused define for msm8960 replaced by generic api and reg_field.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens-8960.c | 24 +-----------------------
+ 1 file changed, 1 insertion(+), 23 deletions(-)
+
+diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
+index 9cc8a7dd23ae..58d09e927383 100644
+--- a/drivers/thermal/qcom/tsens-8960.c
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -10,8 +10,6 @@
+ #include <linux/thermal.h>
+ #include "tsens.h"
+
+-#define CAL_MDEGC 30000
+-
+ #define CONFIG_ADDR 0x3640
+ #define CONFIG_ADDR_8660 0x3620
+ /* CONFIG_ADDR bitmasks */
+@@ -21,39 +19,19 @@
+ #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 MEASURE_PERIOD BIT(18)
+ #define SLP_CLK_ENA BIT(26)
+ #define SLP_CLK_ENA_8660 BIT(24)
+ #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 INT_STATUS_ADDR 0x363c
+-#define TRDY_MASK BIT(7)
+-#define TIMEOUT_US 100
+
+ #define S0_STATUS_OFF 0x3628
+ #define S1_STATUS_OFF 0x362c
+--
+2.30.2
+
--- /dev/null
+From 0d0c22a59bf2672b57e23da9a9ea743e91b71f54 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 25 Jul 2020 19:55:57 +0200
+Subject: [PATCH 08/10] drivers: thermal: tsens: Add support for ipq8064-tsens
+
+Add support for tsens present in ipq806x SoCs based on generic msm8960
+tsens driver.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org>
+---
+ drivers/thermal/qcom/tsens.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
+index 38b9936def1a..58073dc5d30b 100644
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -966,6 +966,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
+
+ static const struct of_device_id tsens_table[] = {
+ {
++ .compatible = "qcom,ipq8064-tsens",
++ .data = &data_8960,
++ }, {
+ .compatible = "qcom,msm8916-tsens",
+ .data = &data_8916,
+ }, {
+--
+2.30.2
+
--- /dev/null
+From ac369071920d427dd484cf74cddba2774bba45f5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 9 Jul 2020 22:35:54 +0200
+Subject: [PATCH 09/10] dt-bindings: thermal: tsens: Document ipq8064 bindings
+
+Document the use of bindings used for msm8960 tsens based devices.
+msm8960 use the same gcc regs and is set as a child of the qcom gcc.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ .../bindings/thermal/qcom-tsens.yaml | 56 ++++++++++++++++---
+ 1 file changed, 48 insertions(+), 8 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+index 95462e071ab4..1785b1c75a3c 100644
+--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+@@ -19,6 +19,11 @@ description: |
+ properties:
+ compatible:
+ oneOf:
++ - description: msm9860 TSENS based
++ items:
++ - enum:
++ - qcom,ipq8064-tsens
++
+ - description: v0.1 of TSENS
+ items:
+ - enum:
+@@ -73,7 +78,9 @@ properties:
+ maxItems: 2
+ items:
+ - const: calib
+- - const: calib_sel
++ - enum:
++ - calib_backup
++ - calib_sel
+
+ "#qcom,sensors":
+ description:
+@@ -88,12 +95,20 @@ properties:
+ Number of cells required to uniquely identify the thermal sensors. Since
+ we have multiple sensors this is set to 1
+
++required:
++ - compatible
++ - interrupts
++ - interrupt-names
++ - "#thermal-sensor-cells"
++ - "#qcom,sensors"
++
+ allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
++ - qcom,ipq8064-tsens
+ - qcom,msm8916-tsens
+ - qcom,msm8974-tsens
+ - qcom,msm8976-tsens
+@@ -114,17 +129,42 @@ allOf:
+ interrupt-names:
+ minItems: 2
+
+-required:
+- - compatible
+- - reg
+- - "#qcom,sensors"
+- - interrupts
+- - interrupt-names
+- - "#thermal-sensor-cells"
++ - if:
++ properties:
++ compatible:
++ contains:
++ enum:
++ - qcom,tsens-v0_1
++ - qcom,tsens-v1
++ - qcom,tsens-v2
++
++ then:
++ required:
++ - reg
+
+ additionalProperties: false
+
+ examples:
++ - |
++ #include <dt-bindings/interrupt-controller/arm-gic.h>
++ // Example msm9860 based SoC (ipq8064):
++ gcc: clock-controller {
++
++ /* ... */
++
++ tsens: thermal-sensor {
++ compatible = "qcom,ipq8064-tsens";
++
++ nvmem-cells = <&tsens_calib>, <&tsens_calib_backup>;
++ nvmem-cell-names = "calib", "calib_backup";
++ interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "uplow";
++
++ #qcom,sensors = <11>;
++ #thermal-sensor-cells = <1>;
++ };
++ };
++
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ // Example 1 (legacy: for pre v1 IP):
+--
+2.30.2
+
--- /dev/null
+From 68e720ed73c8f038c8c500e4c49c1e65a993a448 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 6 Apr 2021 04:45:31 +0200
+Subject: [PATCH 10/10] drivers: thermal: tsens: Fix wrong slope on msm-8960
+
+Some user using some stats with the old legacy implementation and the
+new implementation using the compute_intercept_slope reported an offset
+of 3C. Fix the slope table to reflect the original temp.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ drivers/thermal/qcom/tsens-8960.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
+index 58d09e927383..5cc5b3527f1f 100644
+--- a/drivers/thermal/qcom/tsens-8960.c
++++ b/drivers/thermal/qcom/tsens-8960.c
+@@ -45,11 +45,11 @@
+ #define S9_STATUS_OFF 0x3674
+ #define S10_STATUS_OFF 0x3678
+
+-/* Original slope - 200 to compensate mC to C inaccuracy */
++/* Original slope - 350 to compensate mC to C inaccuracy */
+ static u32 tsens_msm8960_slope[] = {
+- 976, 976, 954, 976,
+- 911, 932, 932, 999,
+- 932, 999, 932
++ 826, 826, 804, 826,
++ 761, 782, 782, 849,
++ 782, 849, 782
+ };
+
+ static int suspend_8960(struct tsens_priv *priv)
+--
+2.30.2
+