628 lines
17 KiB
Diff
628 lines
17 KiB
Diff
|
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, ®);
|
||
|
+ 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,
|
||
|
+};
|
||
|
--- 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__ */
|