summaryrefslogtreecommitdiff
path: root/drivers/thermal/msm-tsens.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/msm-tsens.c')
-rw-r--r--drivers/thermal/msm-tsens.c2680
1 files changed, 2680 insertions, 0 deletions
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
new file mode 100644
index 000000000000..5f955af4671a
--- /dev/null
+++ b/drivers/thermal/msm-tsens.c
@@ -0,0 +1,2680 @@
+/* Copyright (c) 2012-2017, 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/msm_tsens.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
+#include <asm/arch_timer.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/trace_thermal.h>
+
+#define TSENS_DRIVER_NAME "msm-tsens"
+/* TSENS register info */
+#define TSENS_UPPER_LOWER_INTERRUPT_CTRL(n) ((n) + 0x1000)
+#define TSENS_INTERRUPT_EN BIT(0)
+
+#define TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(n) ((n) + 0x1004)
+#define TSENS_UPPER_STATUS_CLR BIT(21)
+#define TSENS_LOWER_STATUS_CLR BIT(20)
+#define TSENS_UPPER_THRESHOLD_MASK 0xffc00
+#define TSENS_LOWER_THRESHOLD_MASK 0x3ff
+#define TSENS_UPPER_THRESHOLD_SHIFT 10
+
+#define TSENS_S0_STATUS_ADDR(n) ((n) + 0x1030)
+#define TSENS_SN_ADDR_OFFSET 0x4
+#define TSENS_SN_STATUS_TEMP_MASK 0x3ff
+#define TSENS_SN_STATUS_LOWER_STATUS BIT(11)
+#define TSENS_SN_STATUS_UPPER_STATUS BIT(12)
+#define TSENS_STATUS_ADDR_OFFSET 2
+
+#define TSENS_TRDY_ADDR(n) ((n) + 0x105c)
+#define TSENS_TRDY_MASK BIT(0)
+
+#define TSENS2_SN_STATUS_ADDR(n) ((n) + 0x1044)
+#define TSENS2_SN_STATUS_VALID BIT(14)
+#define TSENS2_SN_STATUS_VALID_MASK 0x4000
+#define TSENS2_TRDY_ADDR(n) ((n) + 0x84)
+
+#define TSENS4_TRDY_ADDR(n) ((n) + 0x1084)
+
+#define TSENS_MTC_ZONE0_SW_MASK_ADDR(n) ((n) + 0x10c0)
+#define TSENS_TH1_MTC_IN_EFFECT BIT(0)
+#define TSENS_TH2_MTC_IN_EFFECT BIT(1)
+#define TSENS_MTC_IN_EFFECT 0x3
+#define TSENS_MTC_DISABLE 0x0
+
+#define TSENS_MTC_ZONE0_LOG(n) ((n) + 0x10d0)
+#define TSENS_LOGS_VALID_MASK 0x40000000
+#define TSENS_LOGS_VALID_SHIFT 30
+#define TSENS_LOGS_LATEST_MASK 0x0000001f
+#define TSENS_LOGS_LOG1_MASK 0x000003e0
+#define TSENS_LOGS_LOG2_MASK 0x00007c00
+#define TSENS_LOGS_LOG3_MASK 0x000f8000
+#define TSENS_LOGS_LOG4_MASK 0x01f00000
+#define TSENS_LOGS_LOG5_MASK 0x3e000000
+#define TSENS_LOGS_LOG1_SHIFT 5
+#define TSENS_LOGS_LOG2_SHIFT 10
+#define TSENS_LOGS_LOG3_SHIFT 15
+#define TSENS_LOGS_LOG4_SHIFT 20
+#define TSENS_LOGS_LOG5_SHIFT 25
+
+/* TSENS_TM registers for 8996 */
+#define TSENS_TM_INT_EN(n) ((n) + 0x1004)
+#define TSENS_TM_CRITICAL_WD_BARK BIT(31)
+#define TSENS_TM_CRITICAL_CYCLE_MONITOR BIT(30)
+#define TSENS_TM_CRITICAL_INT_EN BIT(2)
+#define TSENS_TM_UPPER_INT_EN BIT(1)
+#define TSENS_TM_LOWER_INT_EN BIT(0)
+
+#define TSENS_TM_UPPER_INT_MASK(n) (((n) & 0xffff0000) >> 16)
+#define TSENS_TM_LOWER_INT_MASK(n) ((n) & 0xffff)
+#define TSENS_TM_UPPER_LOWER_INT_STATUS(n) ((n) + 0x1008)
+#define TSENS_TM_UPPER_LOWER_INT_CLEAR(n) ((n) + 0x100c)
+#define TSENS_TM_UPPER_LOWER_INT_MASK(n) ((n) + 0x1010)
+#define TSENS_TM_UPPER_INT_SET(n) (1 << (n + 16))
+
+#define TSENS_TM_CRITICAL_INT_STATUS(n) ((n) + 0x1014)
+#define TSENS_TM_CRITICAL_INT_CLEAR(n) ((n) + 0x1018)
+#define TSENS_TM_CRITICAL_INT_MASK(n) ((n) + 0x101c)
+
+#define TSENS_TM_UPPER_LOWER_THRESHOLD(n) ((n) + 0x1020)
+#define TSENS_TM_UPPER_THRESHOLD_SET(n) ((n) << 12)
+#define TSENS_TM_UPPER_THRESHOLD_VALUE_SHIFT(n) ((n) >> 12)
+#define TSENS_TM_LOWER_THRESHOLD_VALUE(n) ((n) & 0xfff)
+#define TSENS_TM_UPPER_THRESHOLD_VALUE(n) (((n) & 0xfff000) >> 12)
+#define TSENS_TM_UPPER_THRESHOLD_MASK 0xfff000
+#define TSENS_TM_LOWER_THRESHOLD_MASK 0xfff
+#define TSENS_TM_UPPER_THRESHOLD_SHIFT 12
+
+#define TSENS_TM_SN_CRITICAL_THRESHOLD_MASK 0xfff
+#define TSENS_TM_SN_CRITICAL_THRESHOLD(n) ((n) + 0x1060)
+#define TSENS_TM_SN_STATUS(n) ((n) + 0x10a0)
+#define TSENS_TM_SN_STATUS_VALID_BIT BIT(21)
+#define TSENS_TM_SN_STATUS_CRITICAL_STATUS BIT(19)
+#define TSENS_TM_SN_STATUS_UPPER_STATUS BIT(18)
+#define TSENS_TM_SN_STATUS_LOWER_STATUS BIT(17)
+#define TSENS_TM_SN_LAST_TEMP_MASK 0xfff
+
+#define TSENS_TM_TRDY(n) ((n) + 0x10e4)
+#define TSENS_TM_CODE_BIT_MASK 0xfff
+#define TSENS_TM_CODE_SIGN_BIT 0x800
+
+#define TSENS_CONTROLLER_ID(n) ((n) + 0x1000)
+#define TSENS_DEBUG_CONTROL(n) ((n) + 0x1130)
+#define TSENS_DEBUG_DATA(n) ((n) + 0x1134)
+#define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR(n) ((n) + 0x1140)
+#define TSENS_TM_MTC_ZONE0_LOG(n) ((n) + 0x1150)
+#define TSENS_TM_MTC_ZONE0_HISTORY(n) ((n) + 0x1160)
+#define TSENS_RESET_HISTORY_MASK 0x4
+#define TSENS_RESET_HISTORY_SHIFT 2
+#define TSENS_PS_RED_CMD_MASK 0x3ff00000
+#define TSENS_PS_YELLOW_CMD_MASK 0x000ffc00
+#define TSENS_PS_COOL_CMD_MASK 0x000003ff
+#define TSENS_PS_YELLOW_CMD_SHIFT 0xa
+#define TSENS_PS_RED_CMD_SHIFT 0x14
+/* End TSENS_TM registers for 8996 */
+
+#define TSENS_CTRL_ADDR(n) (n)
+#define TSENS_EN BIT(0)
+
+#define TSENS_CAL_DEGC_POINT1 30
+#define TSENS_CAL_DEGC_POINT2 120
+#define TSENS_SLOPE_FACTOR 1000
+
+/* TSENS register data */
+#define TSENS_TRDY_RDY_MIN_TIME 2000
+#define TSENS_TRDY_RDY_MAX_TIME 2100
+#define TSENS_THRESHOLD_MAX_CODE 0x3ff
+#define TSENS_THRESHOLD_MIN_CODE 0x0
+
+#define TSENS_TYPE0 0
+#define TSENS_TYPE2 2
+#define TSENS_TYPE3 3
+#define TSENS_TYPE4 4
+
+/* debug defines */
+#define TSENS_DBG_BUS_ID_0 0
+#define TSENS_DBG_BUS_ID_1 1
+#define TSENS_DBG_BUS_ID_2 2
+#define TSENS_DBG_BUS_ID_15 15
+#define TSENS_DEBUG_LOOP_COUNT_ID_0 2
+#define TSENS_DEBUG_LOOP_COUNT 5
+#define TSENS_DEBUG_STATUS_REG_START 10
+#define TSENS_DEBUG_OFFSET_RANGE 16
+#define TSENS_DEBUG_OFFSET_WORD1 0x4
+#define TSENS_DEBUG_OFFSET_WORD2 0x8
+#define TSENS_DEBUG_OFFSET_WORD3 0xc
+#define TSENS_DEBUG_OFFSET_ROW 0x10
+#define TSENS_DEBUG_DECIDEGC -950
+#define TSENS_DEBUG_CYCLE_MS 64
+#define TSENS_DEBUG_POLL_MS 200
+#define TSENS_DEBUG_BUS_ID2_MIN_CYCLE 50
+#define TSENS_DEBUG_BUS_ID2_MAX_CYCLE 51
+#define TSENS_DEBUG_ID_MASK_1_4 0xffffffe1
+
+static uint32_t tsens_sec_to_msec_value = 1000;
+static uint32_t tsens_completion_timeout_hz = HZ/2;
+static uint32_t tsens_poll_check = 1;
+
+/* Trips: warm and cool */
+enum tsens_trip_type {
+ TSENS_TRIP_WARM = 0,
+ TSENS_TRIP_COOL,
+ TSENS_TRIP_NUM,
+};
+
+enum tsens_tm_trip_type {
+ TSENS_TM_TRIP_WARM = 0,
+ TSENS_TM_TRIP_COOL,
+ TSENS_TM_TRIP_CRITICAL,
+ TSENS_TM_TRIP_NUM,
+};
+
+#define TSENS_WRITABLE_TRIPS_MASK ((1 << TSENS_TRIP_NUM) - 1)
+#define TSENS_TM_WRITABLE_TRIPS_MASK ((1 << TSENS_TM_TRIP_NUM) - 1)
+
+struct tsens_thrshld_state {
+ enum thermal_device_mode high_th_state;
+ enum thermal_device_mode low_th_state;
+ enum thermal_device_mode crit_th_state;
+ unsigned int high_adc_code;
+ unsigned int low_adc_code;
+ int high_temp;
+ int low_temp;
+ int crit_temp;
+};
+
+struct tsens_tm_device_sensor {
+ struct thermal_zone_device *tz_dev;
+ struct tsens_tm_device *tm;
+ enum thermal_device_mode mode;
+ /* Physical HW sensor number */
+ unsigned int sensor_hw_num;
+ /* Software index. This is keep track of the HW/SW
+ * sensor_ID mapping */
+ unsigned int sensor_sw_id;
+ unsigned int sensor_client_id;
+ int offset;
+ int calib_data_point1;
+ int calib_data_point2;
+ uint32_t slope_mul_tsens_factor;
+ struct tsens_thrshld_state debug_thr_state_copy;
+ /* dbg_adc_code logs either the raw ADC code or temperature values in
+ * decidegC based on the controller settings.
+ */
+ int dbg_adc_code;
+ u32 wa_temp1_calib_offset_factor;
+ u32 wa_temp2_calib_offset_factor;
+};
+
+struct tsens_dbg_counter {
+ uint32_t dbg_count[10];
+ uint32_t idx;
+ unsigned long long time_stmp[10];
+};
+
+struct tsens_sensor_dbg_info {
+ unsigned long temp[10];
+ uint32_t idx;
+ unsigned long long time_stmp[10];
+ int adccode[10];
+};
+
+struct tsens_mtc_sysfs {
+ uint32_t zone_log;
+ int zone_mtc;
+ int th1;
+ int th2;
+ uint32_t zone_hist;
+};
+
+struct tsens_tm_device {
+ struct platform_device *pdev;
+ struct workqueue_struct *tsens_critical_wq;
+ struct list_head list;
+ bool is_ready;
+ bool prev_reading_avail;
+ bool calibration_less_mode;
+ bool tsens_local_init;
+ bool gain_offset_programmed;
+ bool cycle_compltn_monitor;
+ bool wd_bark;
+ int tsens_factor;
+ uint32_t tsens_num_sensor;
+ uint32_t cycle_compltn_monitor_val;
+ uint32_t wd_bark_val;
+ int tsens_irq;
+ int tsens_critical_irq;
+ void *tsens_addr;
+ void *tsens_calib_addr;
+ int tsens_len;
+ int calib_len;
+ struct resource *res_tsens_mem;
+ struct resource *res_calib_mem;
+ uint32_t tsens_type;
+ bool tsens_valid_status_check;
+ struct tsens_dbg_counter tsens_thread_iq_dbg;
+ struct tsens_sensor_dbg_info sensor_dbg_info[16];
+ int tsens_upper_irq_cnt;
+ int tsens_lower_irq_cnt;
+ int tsens_critical_irq_cnt;
+ int tsens_critical_wd_cnt;
+ struct delayed_work tsens_critical_poll_test;
+ struct completion tsens_rslt_completion;
+ struct tsens_mtc_sysfs mtcsys;
+ spinlock_t tsens_crit_lock;
+ spinlock_t tsens_upp_low_lock;
+ bool crit_set;
+ struct tsens_dbg_counter crit_timestamp_last_run;
+ struct tsens_dbg_counter crit_timestamp_last_interrupt_handled;
+ struct tsens_dbg_counter crit_timestamp_last_poll_request;
+ u64 qtimer_val_detection_start;
+ u64 qtimer_val_last_detection_interrupt;
+ u64 qtimer_val_last_polling_check;
+ bool tsens_critical_poll;
+ struct tsens_tm_device_sensor sensor[0];
+};
+
+LIST_HEAD(tsens_device_list);
+
+static char dbg_buff[1024];
+static struct dentry *dent;
+static struct dentry *dfile_stats;
+
+static struct of_device_id tsens_match[] = {
+ { .compatible = "qcom,msm8996-tsens",
+ },
+ { .compatible = "qcom,msmtitanium-tsens",
+ },
+ { .compatible = "qcom,msm8998-tsens",
+ },
+ { .compatible = "qcom,msmhamster-tsens",
+ },
+ { .compatible = "qcom,sdm660-tsens",
+ },
+ { .compatible = "qcom,sdm630-tsens",
+ },
+ {}
+};
+
+static struct tsens_tm_device *tsens_controller_is_present(void)
+{
+ struct tsens_tm_device *tmdev_chip = NULL;
+
+ if (list_empty(&tsens_device_list)) {
+ pr_err("%s: TSENS controller not available\n", __func__);
+ return tmdev_chip;
+ }
+
+ list_for_each_entry(tmdev_chip, &tsens_device_list, list)
+ return tmdev_chip;
+
+ return tmdev_chip;
+}
+
+static int32_t get_tsens_sensor_for_client_id(struct tsens_tm_device *tmdev,
+ uint32_t sensor_client_id)
+{
+ bool id_found = false;
+ uint32_t i = 0;
+ struct device_node *of_node = NULL;
+ const struct of_device_id *id;
+
+ of_node = tmdev->pdev->dev.of_node;
+ if (of_node == NULL) {
+ pr_err("Invalid of_node??\n");
+ return -EINVAL;
+ }
+
+ if (!of_match_node(tsens_match, of_node)) {
+ pr_err("Need to read SoC specific fuse map\n");
+ return -ENODEV;
+ }
+
+ id = of_match_node(tsens_match, of_node);
+ if (id == NULL) {
+ pr_err("can not find tsens_match of_node\n");
+ return -ENODEV;
+ }
+
+ if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
+ (!strcmp(id->compatible, "qcom,msm8998-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm660-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm630-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens"))) {
+ while (i < tmdev->tsens_num_sensor && !id_found) {
+ if (tmdev->sensor[i].sensor_client_id ==
+ sensor_client_id) {
+ id_found = true;
+ return tmdev->sensor[i].sensor_hw_num;
+ }
+ i++;
+ }
+ } else
+ return sensor_client_id;
+
+ if (!id_found)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+
+static struct tsens_tm_device *get_tsens_controller_for_client_id(
+ uint32_t sensor_client_id)
+{
+ struct tsens_tm_device *tmdev_chip = NULL;
+ bool id_found = false;
+ uint32_t i = 0;
+
+ list_for_each_entry(tmdev_chip, &tsens_device_list, list) {
+ i = 0;
+ while (i < tmdev_chip->tsens_num_sensor && !id_found) {
+ if (tmdev_chip->sensor[i].sensor_client_id ==
+ sensor_client_id) {
+ id_found = true;
+ return tmdev_chip;
+ }
+ i++;
+ }
+ }
+
+ if (!id_found)
+ return NULL;
+
+ return tmdev_chip;
+}
+
+static struct tsens_tm_device *get_all_tsens_controller_sensor_count(
+ uint32_t *sensor_count)
+{
+ struct tsens_tm_device *tmdev_chip = NULL;
+
+ list_for_each_entry(tmdev_chip, &tsens_device_list, list)
+ *sensor_count += tmdev_chip->tsens_num_sensor;
+
+ return tmdev_chip;
+}
+
+int tsens_is_ready(void)
+{
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev)
+ return -EPROBE_DEFER;
+ else
+ return tmdev->is_ready;
+}
+EXPORT_SYMBOL(tsens_is_ready);
+
+static int tsens_get_sw_id_mapping_for_controller(
+ int sensor_hw_num,
+ int *sensor_sw_idx,
+ struct tsens_tm_device *tmdev)
+{
+ int i = 0;
+ bool id_found = false;
+
+ while (i < tmdev->tsens_num_sensor && !id_found) {
+ if (sensor_hw_num == tmdev->sensor[i].sensor_hw_num) {
+ *sensor_sw_idx = tmdev->sensor[i].sensor_sw_id;
+ id_found = true;
+ }
+ i++;
+ }
+
+ if (!id_found)
+ return -EINVAL;
+
+ return 0;
+}
+
+int tsens_get_hw_id_mapping(int thermal_sensor_num, int *sensor_client_id)
+{
+ struct tsens_tm_device *tmdev = NULL;
+ struct device_node *of_node = NULL;
+ const struct of_device_id *id;
+ uint32_t tsens_max_sensors = 0, idx = 0, i = 0;
+
+ if (list_empty(&tsens_device_list)) {
+ pr_err("%s: TSENS controller not available\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ list_for_each_entry(tmdev, &tsens_device_list, list)
+ tsens_max_sensors += tmdev->tsens_num_sensor;
+
+ if (tsens_max_sensors != thermal_sensor_num) {
+ pr_err("TSENS total sensors is %d, thermal expects:%d\n",
+ tsens_max_sensors, thermal_sensor_num);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(tmdev, &tsens_device_list, list) {
+ of_node = tmdev->pdev->dev.of_node;
+ if (of_node == NULL) {
+ pr_err("Invalid of_node??\n");
+ return -EINVAL;
+ }
+
+ if (!of_match_node(tsens_match, of_node)) {
+ pr_err("Need to read SoC specific fuse map\n");
+ return -ENODEV;
+ }
+
+ id = of_match_node(tsens_match, of_node);
+ if (id == NULL) {
+ pr_err("can not find tsens_match of_node\n");
+ return -ENODEV;
+ }
+
+ if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
+ (!strcmp(id->compatible, "qcom,msm8998-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm660-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm630-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens"))) {
+ /* Assign client id's that is used to get the
+ * controller and hw_sensor details
+ */
+ for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+ sensor_client_id[idx] =
+ tmdev->sensor[i].sensor_client_id;
+ idx++;
+ }
+ } else {
+ /* Assign the corresponding hw sensor number
+ * prior to support for multiple controllres
+ */
+ for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+ sensor_client_id[idx] =
+ tmdev->sensor[i].sensor_hw_num;
+ idx++;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_hw_id_mapping);
+
+static ssize_t
+zonemask_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ return snprintf(buf, PAGE_SIZE,
+ "Zone =%d th1=%d th2=%d\n" , tmdev->mtcsys.zone_mtc,
+ tmdev->mtcsys.th1 , tmdev->mtcsys.th2);
+}
+
+static ssize_t
+zonemask_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = sscanf(buf, "%d %d %d", &tmdev->mtcsys.zone_mtc ,
+ &tmdev->mtcsys.th1 , &tmdev->mtcsys.th2);
+
+ if (ret != TSENS_ZONEMASK_PARAMS) {
+ pr_err("Invalid command line arguments\n");
+ count = -EINVAL;
+ } else {
+ pr_debug("store zone_mtc=%d th1=%d th2=%d\n",
+ tmdev->mtcsys.zone_mtc,
+ tmdev->mtcsys.th1 , tmdev->mtcsys.th2);
+ ret = tsens_set_mtc_zone_sw_mask(tmdev->mtcsys.zone_mtc ,
+ tmdev->mtcsys.th1 , tmdev->mtcsys.th2);
+ if (ret < 0) {
+ pr_err("Invalid command line arguments\n");
+ count = -EINVAL;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t
+zonelog_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret, zlog[TSENS_MTC_ZONE_LOG_SIZE];
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = tsens_get_mtc_zone_log(tmdev->mtcsys.zone_log , zlog);
+ if (ret < 0) {
+ pr_err("Invalid command line arguments\n");
+ return -EINVAL;
+ }
+
+ return snprintf(buf, PAGE_SIZE,
+ "Log[0]=%d\nLog[1]=%d\nLog[2]=%d\nLog[3]=%d\nLog[4]=%d\nLog[5]=%d\n",
+ zlog[0], zlog[1], zlog[2], zlog[3], zlog[4], zlog[5]);
+}
+
+static ssize_t
+zonelog_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = kstrtou32(buf, 0, &tmdev->mtcsys.zone_log);
+ if (ret < 0) {
+ pr_err("Invalid command line arguments\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t
+zonehist_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret, zhist[TSENS_MTC_ZONE_HISTORY_SIZE];
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = tsens_get_mtc_zone_history(tmdev->mtcsys.zone_hist , zhist);
+ if (ret < 0) {
+ pr_err("Invalid command line arguments\n");
+ return -EINVAL;
+ }
+
+ return snprintf(buf, PAGE_SIZE,
+ "Cool = %d\nYellow = %d\nRed = %d\n",
+ zhist[0], zhist[1], zhist[2]);
+}
+
+static ssize_t
+zonehist_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct tsens_tm_device *tmdev = NULL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = kstrtou32(buf, 0, &tmdev->mtcsys.zone_hist);
+ if (ret < 0) {
+ pr_err("Invalid command line arguments\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+
+static struct device_attribute tsens_mtc_dev_attr[] = {
+ __ATTR(zonemask, 0644, zonemask_show, zonemask_store),
+ __ATTR(zonelog, 0644, zonelog_show, zonelog_store),
+ __ATTR(zonehist, 0644, zonehist_show, zonehist_store),
+};
+
+static int create_tsens_mtc_sysfs(struct platform_device *pdev)
+{
+ int result = 0, i;
+ struct device_attribute *attr_ptr = NULL;
+
+ attr_ptr = tsens_mtc_dev_attr;
+
+ for (i = 0; i < ARRAY_SIZE(tsens_mtc_dev_attr); i++) {
+ result = device_create_file(&pdev->dev, &attr_ptr[i]);
+ if (result < 0)
+ goto error;
+ }
+
+ pr_debug("create_tsens_mtc_sysfs success\n");
+
+ return result;
+
+error:
+ for (i--; i >= 0; i--)
+ device_remove_file(&pdev->dev, &attr_ptr[i]);
+
+ return result;
+}
+
+static int tsens_tz_code_to_degc(int adc_code, int sensor_sw_id,
+ struct tsens_tm_device *tmdev)
+{
+ int degc, num, den, idx;
+
+ idx = sensor_sw_id;
+ num = ((adc_code * tmdev->tsens_factor) -
+ tmdev->sensor[idx].offset);
+ den = (int) tmdev->sensor[idx].slope_mul_tsens_factor;
+
+ if (num > 0)
+ degc = ((num + (den/2))/den);
+ else if (num < 0)
+ degc = ((num - (den/2))/den);
+ else
+ degc = num/den;
+
+ pr_debug("raw_code:0x%x, sensor_num:%d, degc:%d, offset:%d\n",
+ adc_code, idx, degc, tmdev->sensor[idx].offset);
+
+ return degc;
+}
+
+static int tsens_tz_degc_to_code(int degc, int idx,
+ struct tsens_tm_device *tmdev)
+{
+ int code = ((degc * tmdev->sensor[idx].slope_mul_tsens_factor)
+ + tmdev->sensor[idx].offset)/tmdev->tsens_factor;
+
+ if (code > TSENS_THRESHOLD_MAX_CODE)
+ code = TSENS_THRESHOLD_MAX_CODE;
+ else if (code < TSENS_THRESHOLD_MIN_CODE)
+ code = TSENS_THRESHOLD_MIN_CODE;
+ pr_debug("raw_code:0x%x, sensor_num:%d, degc:%d\n",
+ code, idx, degc);
+ return code;
+}
+
+static int msm_tsens_get_temp(int sensor_client_id, int *temp)
+{
+ unsigned int code;
+ void __iomem *sensor_addr;
+ void __iomem *trdy_addr;
+ int sensor_sw_id = -EINVAL, rc = 0, last_temp = 0, last_temp2 = 0;
+ int last_temp3 = 0, last_temp_mask, valid_status_mask, code_mask = 0;
+ bool last_temp_valid = false, last_temp2_valid = false;
+ bool last_temp3_valid = false;
+ struct tsens_tm_device *tmdev = NULL;
+ uint32_t sensor_hw_num = 0;
+
+ tmdev = get_tsens_controller_for_client_id(sensor_client_id);
+ if (tmdev == NULL) {
+ pr_err("TSENS early init not done\n");
+ return -EPROBE_DEFER;
+ }
+
+ pr_debug("sensor_client_id:%d\n", sensor_client_id);
+
+ sensor_hw_num = get_tsens_sensor_for_client_id(tmdev, sensor_client_id);
+ if (sensor_hw_num < 0) {
+ pr_err("cannot read the temperature\n");
+ return sensor_hw_num;
+ }
+ pr_debug("sensor_hw_num:%d\n", sensor_hw_num);
+
+ if (tmdev->tsens_type == TSENS_TYPE2) {
+ trdy_addr = TSENS2_TRDY_ADDR(tmdev->tsens_addr);
+ sensor_addr = TSENS2_SN_STATUS_ADDR(tmdev->tsens_addr);
+ } else if (tmdev->tsens_type == TSENS_TYPE3) {
+ trdy_addr = TSENS_TM_TRDY(tmdev->tsens_addr);
+ sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_addr);
+ } else if (tmdev->tsens_type == TSENS_TYPE4) {
+ trdy_addr = TSENS4_TRDY_ADDR(tmdev->tsens_addr);
+ sensor_addr = TSENS2_SN_STATUS_ADDR(tmdev->tsens_addr);
+ } else {
+ trdy_addr = TSENS_TRDY_ADDR(tmdev->tsens_addr);
+ sensor_addr = TSENS_S0_STATUS_ADDR(tmdev->tsens_addr);
+ }
+
+ if ((!tmdev->prev_reading_avail) && !tmdev->tsens_valid_status_check) {
+ while (!((readl_relaxed_no_log(trdy_addr)) & TSENS_TRDY_MASK))
+ usleep_range(TSENS_TRDY_RDY_MIN_TIME,
+ TSENS_TRDY_RDY_MAX_TIME);
+ tmdev->prev_reading_avail = true;
+ }
+
+ if (tmdev->tsens_type == TSENS_TYPE3)
+ last_temp_mask = TSENS_TM_SN_LAST_TEMP_MASK;
+ else
+ last_temp_mask = TSENS_SN_STATUS_TEMP_MASK;
+
+ code = readl_relaxed_no_log(sensor_addr +
+ (sensor_hw_num << TSENS_STATUS_ADDR_OFFSET));
+ last_temp = code & last_temp_mask;
+
+ if (tmdev->tsens_valid_status_check) {
+ if (tmdev->tsens_type == TSENS_TYPE3)
+ valid_status_mask = TSENS_TM_SN_STATUS_VALID_BIT;
+ else
+ valid_status_mask = TSENS2_SN_STATUS_VALID;
+ if (code & valid_status_mask)
+ last_temp_valid = true;
+ else {
+ code = readl_relaxed_no_log(sensor_addr +
+ (sensor_hw_num << TSENS_STATUS_ADDR_OFFSET));
+ last_temp2 = code & last_temp_mask;
+ if (code & valid_status_mask) {
+ last_temp = last_temp2;
+ last_temp2_valid = true;
+ } else {
+ code = readl_relaxed_no_log(sensor_addr +
+ (sensor_hw_num <<
+ TSENS_STATUS_ADDR_OFFSET));
+ last_temp3 = code & last_temp_mask;
+ if (code & valid_status_mask) {
+ last_temp = last_temp3;
+ last_temp3_valid = true;
+ }
+ }
+ }
+ }
+
+ if ((tmdev->tsens_valid_status_check) &&
+ (!last_temp_valid && !last_temp2_valid && !last_temp3_valid)) {
+ if (last_temp == last_temp2)
+ last_temp = last_temp2;
+ else if (last_temp2 == last_temp3)
+ last_temp = last_temp3;
+ }
+
+ if (tmdev->tsens_type != TSENS_TYPE3) {
+ /* Obtain SW index to map the corresponding thermal zone's
+ * offset and slope for code to degc conversion. */
+ rc = tsens_get_sw_id_mapping_for_controller(sensor_hw_num,
+ &sensor_sw_id, tmdev);
+ if (rc < 0) {
+ pr_err("tsens mapping index not found\n");
+ return rc;
+ }
+
+ *temp = tsens_tz_code_to_degc(last_temp, sensor_sw_id, tmdev);
+ } else {
+ if (last_temp & TSENS_TM_CODE_SIGN_BIT) {
+ /* Sign extension for negative value */
+ code_mask = ~TSENS_TM_CODE_BIT_MASK;
+ last_temp |= code_mask;
+ }
+ *temp = last_temp;
+ }
+
+ tmdev->sensor[sensor_hw_num].dbg_adc_code = last_temp;
+
+ trace_tsens_read(*temp, sensor_client_id);
+
+ return 0;
+}
+
+static int tsens_tz_get_temp(struct thermal_zone_device *thermal,
+ int *temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ struct tsens_tm_device *tmdev = NULL;
+ uint32_t idx = 0;
+ int rc = 0;
+
+ if (!tm_sensor || !temp)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ rc = msm_tsens_get_temp(tm_sensor->sensor_client_id, temp);
+ if (rc)
+ return rc;
+
+ idx = tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].idx;
+ tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].temp[idx%10] = *temp;
+ tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].time_stmp[idx%10] =
+ sched_clock();
+ tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].adccode[idx%10] =
+ tmdev->sensor[tm_sensor->sensor_hw_num].dbg_adc_code;
+ idx++;
+ tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].idx = idx;
+
+ return 0;
+}
+
+int tsens_get_temp(struct tsens_device *device, int *temp)
+{
+ int rc = 0;
+
+ if (tsens_is_ready() <= 0) {
+ pr_debug("TSENS early init not done\n");
+ return -EPROBE_DEFER;
+ }
+
+ rc = msm_tsens_get_temp(device->sensor_num, temp);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_temp);
+
+int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors)
+{
+ if (tsens_is_ready() <= 0) {
+ pr_debug("TSENS early init not done\n");
+ return -EPROBE_DEFER;
+ }
+
+ *tsens_num_sensors = 0;
+
+ if (get_all_tsens_controller_sensor_count(tsens_num_sensors) == NULL)
+ return -EINVAL;
+
+ pr_debug("%d\n", *tsens_num_sensors);
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_max_sensor_num);
+
+static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+ if (!tm_sensor || !mode)
+ return -EINVAL;
+
+ *mode = tm_sensor->mode;
+
+ return 0;
+}
+
+static int tsens_tz_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+ if (!tm_sensor || trip < 0 || !type)
+ return -EINVAL;
+
+ switch (trip) {
+ case TSENS_TRIP_WARM:
+ *type = THERMAL_TRIP_CONFIGURABLE_HI;
+ break;
+ case TSENS_TRIP_COOL:
+ *type = THERMAL_TRIP_CONFIGURABLE_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tsens_tm_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+ if (!tm_sensor || trip < 0 || !type)
+ return -EINVAL;
+
+ switch (trip) {
+ case TSENS_TM_TRIP_WARM:
+ *type = THERMAL_TRIP_CONFIGURABLE_HI;
+ break;
+ case TSENS_TM_TRIP_COOL:
+ *type = THERMAL_TRIP_CONFIGURABLE_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tsens_tm_activate_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_activation_mode mode)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ unsigned int reg_cntl, mask;
+ unsigned long flags;
+ struct tsens_tm_device *tmdev = NULL;
+ int rc = 0;
+
+ /* clear the interrupt and unmask */
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
+ mask = (tm_sensor->sensor_hw_num);
+ switch (trip) {
+ case TSENS_TM_TRIP_CRITICAL:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.crit_th_state = mode;
+ reg_cntl = readl_relaxed(TSENS_TM_CRITICAL_INT_MASK
+ (tmdev->tsens_addr));
+ if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
+ writel_relaxed(reg_cntl | (1 << mask),
+ (TSENS_TM_CRITICAL_INT_MASK
+ (tmdev->tsens_addr)));
+ else
+ writel_relaxed(reg_cntl & ~(1 << mask),
+ (TSENS_TM_CRITICAL_INT_MASK
+ (tmdev->tsens_addr)));
+ break;
+ case TSENS_TM_TRIP_WARM:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.high_th_state = mode;
+ reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
+ (tmdev->tsens_addr));
+ if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
+ writel_relaxed(reg_cntl |
+ (TSENS_TM_UPPER_INT_SET(mask)),
+ (TSENS_TM_UPPER_LOWER_INT_MASK
+ (tmdev->tsens_addr)));
+ else
+ writel_relaxed(reg_cntl &
+ ~(TSENS_TM_UPPER_INT_SET(mask)),
+ (TSENS_TM_UPPER_LOWER_INT_MASK
+ (tmdev->tsens_addr)));
+ break;
+ case TSENS_TM_TRIP_COOL:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.low_th_state = mode;
+ reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
+ (tmdev->tsens_addr));
+ if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
+ writel_relaxed(reg_cntl | (1 << mask),
+ (TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_addr)));
+ else
+ writel_relaxed(reg_cntl & ~(1 << mask),
+ (TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_addr)));
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
+ /* Activate and enable the respective trip threshold setting */
+ mb();
+
+ return rc;
+}
+
+static int tsens_tz_activate_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_activation_mode mode)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ unsigned int reg_cntl, code, hi_code, lo_code, mask;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ lo_code = TSENS_THRESHOLD_MIN_CODE;
+ hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+ reg_cntl = readl_relaxed((TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET)));
+
+ switch (trip) {
+ case TSENS_TRIP_WARM:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.high_th_state = mode;
+
+ code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
+ >> TSENS_UPPER_THRESHOLD_SHIFT;
+ mask = TSENS_UPPER_STATUS_CLR;
+
+ if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+ lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
+ break;
+ case TSENS_TRIP_COOL:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.low_th_state = mode;
+
+ code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
+ mask = TSENS_LOWER_STATUS_CLR;
+
+ if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+ hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
+ >> TSENS_UPPER_THRESHOLD_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
+ writel_relaxed(reg_cntl | mask,
+ (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET)));
+
+ else
+ writel_relaxed(reg_cntl & ~mask,
+ (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET)));
+ /* Enable the thresholds */
+ mb();
+ return 0;
+}
+
+static int tsens_tm_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, int *temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ int reg_cntl, code_mask;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (!tm_sensor || trip < 0 || !temp)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ switch (trip) {
+ case TSENS_TM_TRIP_CRITICAL:
+ reg_cntl = readl_relaxed((TSENS_TM_SN_CRITICAL_THRESHOLD
+ (tmdev->tsens_addr)) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET));
+ if (reg_cntl & TSENS_TM_CODE_SIGN_BIT) {
+ /* Sign extension for negative value */
+ code_mask = ~TSENS_TM_CODE_BIT_MASK;
+ reg_cntl |= code_mask;
+ }
+ break;
+ case TSENS_TM_TRIP_WARM:
+ reg_cntl = readl_relaxed((TSENS_TM_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_addr)) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET));
+ reg_cntl = TSENS_TM_UPPER_THRESHOLD_VALUE(reg_cntl);
+ if (reg_cntl & TSENS_TM_CODE_SIGN_BIT) {
+ /* Sign extension for negative value */
+ code_mask = ~TSENS_TM_CODE_BIT_MASK;
+ reg_cntl |= code_mask;
+ }
+ break;
+ case TSENS_TM_TRIP_COOL:
+ reg_cntl = readl_relaxed((TSENS_TM_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_addr)) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET));
+ reg_cntl = TSENS_TM_LOWER_THRESHOLD_VALUE(reg_cntl);
+ if (reg_cntl & TSENS_TM_CODE_SIGN_BIT) {
+ /* Sign extension for negative value */
+ code_mask = ~TSENS_TM_CODE_BIT_MASK;
+ reg_cntl |= code_mask;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *temp = reg_cntl;
+
+ return 0;
+}
+
+static int tsens_tz_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, int *temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ unsigned int reg;
+ int sensor_sw_id = -EINVAL, rc = 0;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (!tm_sensor || trip < 0 || !temp)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ reg = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET));
+ switch (trip) {
+ case TSENS_TRIP_WARM:
+ reg = (reg & TSENS_UPPER_THRESHOLD_MASK) >>
+ TSENS_UPPER_THRESHOLD_SHIFT;
+ break;
+ case TSENS_TRIP_COOL:
+ reg = (reg & TSENS_LOWER_THRESHOLD_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = tsens_get_sw_id_mapping_for_controller(tm_sensor->sensor_hw_num,
+ &sensor_sw_id, tmdev);
+ if (rc < 0) {
+ pr_err("tsens mapping index not found\n");
+ return rc;
+ }
+ *temp = tsens_tz_code_to_degc(reg, sensor_sw_id, tmdev);
+
+ return 0;
+}
+
+static int tsens_tz_notify(struct thermal_zone_device *thermal,
+ int count, enum thermal_trip_type type)
+{
+ /* Critical temperature threshold are enabled and will
+ * shutdown the device once critical thresholds are crossed. */
+ pr_debug("%s debug\n", __func__);
+ return 1;
+}
+
+static int tsens_tm_set_trip_temp(struct thermal_zone_device *thermal,
+ int trip, int temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ unsigned int reg_cntl;
+ unsigned long flags;
+ struct tsens_tm_device *tmdev = NULL;
+ int rc = 0;
+
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
+ switch (trip) {
+ case TSENS_TM_TRIP_CRITICAL:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.crit_temp = temp;
+ temp &= TSENS_TM_SN_CRITICAL_THRESHOLD_MASK;
+ writel_relaxed(temp,
+ (TSENS_TM_SN_CRITICAL_THRESHOLD(tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET)));
+ break;
+ case TSENS_TM_TRIP_WARM:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.high_temp = temp;
+ reg_cntl = readl_relaxed((TSENS_TM_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_addr)) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET));
+ temp = TSENS_TM_UPPER_THRESHOLD_SET(temp);
+ temp &= TSENS_TM_UPPER_THRESHOLD_MASK;
+ reg_cntl &= ~TSENS_TM_UPPER_THRESHOLD_MASK;
+ writel_relaxed(reg_cntl | temp,
+ (TSENS_TM_UPPER_LOWER_THRESHOLD(tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET)));
+ break;
+ case TSENS_TM_TRIP_COOL:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.low_temp = temp;
+ reg_cntl = readl_relaxed((TSENS_TM_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_addr)) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET));
+ temp &= TSENS_TM_LOWER_THRESHOLD_MASK;
+ reg_cntl &= ~TSENS_TM_LOWER_THRESHOLD_MASK;
+ writel_relaxed(reg_cntl | temp,
+ (TSENS_TM_UPPER_LOWER_THRESHOLD(tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET)));
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
+ /* Set trip temperature thresholds */
+ mb();
+ return rc;
+}
+
+static int tsens_tz_set_trip_temp(struct thermal_zone_device *thermal,
+ int trip, int temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ unsigned int reg_cntl;
+ int code, hi_code, lo_code, code_err_chk, sensor_sw_id = 0, rc = 0;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ tmdev = tm_sensor->tm;
+ if (!tmdev)
+ return -EINVAL;
+
+ rc = tsens_get_sw_id_mapping_for_controller(tm_sensor->sensor_hw_num,
+ &sensor_sw_id, tmdev);
+ if (rc < 0) {
+ pr_err("tsens mapping index not found\n");
+ return rc;
+ }
+
+ code_err_chk = code = tsens_tz_degc_to_code(temp, sensor_sw_id, tmdev);
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ lo_code = TSENS_THRESHOLD_MIN_CODE;
+ hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+ reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_addr) + (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET));
+ switch (trip) {
+ case TSENS_TRIP_WARM:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.high_adc_code = code;
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.high_temp = temp;
+ code <<= TSENS_UPPER_THRESHOLD_SHIFT;
+ reg_cntl &= ~TSENS_UPPER_THRESHOLD_MASK;
+ if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+ lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
+ break;
+ case TSENS_TRIP_COOL:
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.low_adc_code = code;
+ tmdev->sensor[tm_sensor->sensor_hw_num].
+ debug_thr_state_copy.low_temp = temp;
+ reg_cntl &= ~TSENS_LOWER_THRESHOLD_MASK;
+ if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+ hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
+ >> TSENS_UPPER_THRESHOLD_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel_relaxed(reg_cntl | code, (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
+ (tmdev->tsens_addr) +
+ (tm_sensor->sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET)));
+ /* Activate the set trip temperature thresholds */
+ mb();
+ return 0;
+}
+
+static void tsens_poll(struct work_struct *work)
+{
+ struct tsens_tm_device *tmdev = container_of(work,
+ struct tsens_tm_device, tsens_critical_poll_test.work);
+ unsigned int reg_cntl, mask, rc = 0, debug_dump, i = 0, loop = 0;
+ unsigned int debug_id = 0, cntrl_id = 0;
+ uint32_t r1, r2, r3, r4, offset = 0, idx = 0;
+ unsigned long temp, flags;
+ unsigned int status, int_mask, int_mask_val;
+ void __iomem *srot_addr;
+ void __iomem *controller_id_addr;
+ void __iomem *debug_id_addr;
+ void __iomem *debug_data_addr;
+ void __iomem *sensor_status_addr;
+ void __iomem *sensor_int_mask_addr;
+ void __iomem *sensor_critical_addr;
+
+ /* Set the Critical temperature threshold to a value of 10 that should
+ * guarantee a threshold to trigger. Check the interrupt count if
+ * it did. Schedule the next round of the above test again after
+ * 3 seconds.
+ */
+
+ controller_id_addr = TSENS_CONTROLLER_ID(tmdev->tsens_addr);
+ debug_id_addr = TSENS_DEBUG_CONTROL(tmdev->tsens_addr);
+ debug_data_addr = TSENS_DEBUG_DATA(tmdev->tsens_addr);
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_addr);
+
+ temp = TSENS_DEBUG_DECIDEGC;
+ /* Sensor 0 on either of the controllers */
+ mask = 0;
+
+ reinit_completion(&tmdev->tsens_rslt_completion);
+
+ temp &= TSENS_TM_SN_CRITICAL_THRESHOLD_MASK;
+ writel_relaxed(temp,
+ (TSENS_TM_SN_CRITICAL_THRESHOLD(tmdev->tsens_addr) +
+ (mask * TSENS_SN_ADDR_OFFSET)));
+
+ /* debug */
+ idx = tmdev->crit_timestamp_last_run.idx;
+ tmdev->crit_timestamp_last_run.time_stmp[idx%10] = sched_clock();
+ tmdev->crit_timestamp_last_run.idx++;
+ tmdev->qtimer_val_detection_start = arch_counter_get_cntvct();
+
+ spin_lock_irqsave(&tmdev->tsens_crit_lock, flags);
+ /* Clear the sensor0 critical status */
+ int_mask_val = 1;
+ writel_relaxed(int_mask_val,
+ TSENS_TM_CRITICAL_INT_CLEAR(tmdev->tsens_addr));
+ writel_relaxed(0,
+ TSENS_TM_CRITICAL_INT_CLEAR(
+ tmdev->tsens_addr));
+ /* Clear the status */
+ mb();
+ tmdev->crit_set = true;
+ if (!tmdev->tsens_critical_poll) {
+ reg_cntl = readl_relaxed(
+ TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_addr));
+ writel_relaxed(reg_cntl & ~(1 << mask),
+ (TSENS_TM_CRITICAL_INT_MASK
+ (tmdev->tsens_addr)));
+ /* Enable the critical int mask */
+ mb();
+ }
+ spin_unlock_irqrestore(&tmdev->tsens_crit_lock, flags);
+
+ if (tmdev->tsens_critical_poll) {
+ msleep(TSENS_DEBUG_POLL_MS);
+ sensor_status_addr = TSENS_TM_SN_STATUS(tmdev->tsens_addr);
+
+ spin_lock_irqsave(&tmdev->tsens_crit_lock, flags);
+ status = readl_relaxed(sensor_status_addr);
+ spin_unlock_irqrestore(&tmdev->tsens_crit_lock, flags);
+
+ if (status & TSENS_TM_SN_STATUS_CRITICAL_STATUS)
+ goto re_schedule;
+ else {
+ pr_err("status:0x%x\n", status);
+ goto debug_start;
+ }
+ }
+
+ rc = wait_for_completion_timeout(
+ &tmdev->tsens_rslt_completion,
+ tsens_completion_timeout_hz);
+ if (!rc) {
+ pr_debug("Switch to polling, TSENS critical interrupt failed\n");
+ sensor_status_addr = TSENS_TM_SN_STATUS(tmdev->tsens_addr);
+ sensor_int_mask_addr =
+ TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_addr);
+ sensor_critical_addr =
+ TSENS_TM_SN_CRITICAL_THRESHOLD(tmdev->tsens_addr);
+
+ spin_lock_irqsave(&tmdev->tsens_crit_lock, flags);
+ if (!tmdev->crit_set) {
+ pr_debug("Ignore this check cycle\n");
+ spin_unlock_irqrestore(&tmdev->tsens_crit_lock, flags);
+ goto re_schedule;
+ }
+ status = readl_relaxed(sensor_status_addr);
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+ tmdev->crit_set = false;
+ spin_unlock_irqrestore(&tmdev->tsens_crit_lock, flags);
+
+ idx = tmdev->crit_timestamp_last_poll_request.idx;
+ tmdev->crit_timestamp_last_poll_request.time_stmp[idx%10] =
+ sched_clock();
+ tmdev->crit_timestamp_last_poll_request.idx++;
+ tmdev->qtimer_val_last_polling_check =
+ arch_counter_get_cntvct();
+ if (status & TSENS_TM_SN_STATUS_CRITICAL_STATUS) {
+
+ spin_lock_irqsave(&tmdev->tsens_crit_lock, flags);
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+ int_mask_val = 1;
+ /* Mask the corresponding interrupt for the sensors */
+ writel_relaxed(int_mask | int_mask_val,
+ TSENS_TM_CRITICAL_INT_MASK(
+ tmdev->tsens_addr));
+ /* Clear the corresponding sensors interrupt */
+ writel_relaxed(int_mask_val,
+ TSENS_TM_CRITICAL_INT_CLEAR(tmdev->tsens_addr));
+ writel_relaxed(0,
+ TSENS_TM_CRITICAL_INT_CLEAR(
+ tmdev->tsens_addr));
+ spin_unlock_irqrestore(&tmdev->tsens_crit_lock, flags);
+
+ /* Clear critical status */
+ mb();
+ goto re_schedule;
+ }
+
+debug_start:
+ cntrl_id = readl_relaxed(controller_id_addr);
+ pr_err("Controller_id: 0x%x\n", cntrl_id);
+
+ loop = 0;
+ i = 0;
+ debug_id = readl_relaxed(debug_id_addr);
+ writel_relaxed((debug_id | (i << 1) | 1),
+ TSENS_DEBUG_CONTROL(tmdev->tsens_addr));
+ while (loop < TSENS_DEBUG_LOOP_COUNT_ID_0) {
+ debug_dump = readl_relaxed(debug_data_addr);
+ r1 = readl_relaxed(debug_data_addr);
+ r2 = readl_relaxed(debug_data_addr);
+ r3 = readl_relaxed(debug_data_addr);
+ r4 = readl_relaxed(debug_data_addr);
+ pr_err("cntrl:%d, bus-id:%d value:0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ cntrl_id, i, debug_dump, r1, r2, r3, r4);
+ loop++;
+ }
+
+ for (i = TSENS_DBG_BUS_ID_1; i <= TSENS_DBG_BUS_ID_15; i++) {
+ loop = 0;
+ debug_id = readl_relaxed(debug_id_addr);
+ debug_id = debug_id & TSENS_DEBUG_ID_MASK_1_4;
+ writel_relaxed((debug_id | (i << 1) | 1),
+ TSENS_DEBUG_CONTROL(tmdev->tsens_addr));
+ while (loop < TSENS_DEBUG_LOOP_COUNT) {
+ debug_dump = readl_relaxed(debug_data_addr);
+ pr_err("cntrl:%d, bus-id:%d with value: 0x%x\n",
+ cntrl_id, i, debug_dump);
+ if (i == TSENS_DBG_BUS_ID_2)
+ usleep_range(
+ TSENS_DEBUG_BUS_ID2_MIN_CYCLE,
+ TSENS_DEBUG_BUS_ID2_MAX_CYCLE);
+ loop++;
+ }
+ }
+
+ pr_err("Start of TSENS TM dump\n");
+ for (i = 0; i < TSENS_DEBUG_OFFSET_RANGE; i++) {
+ r1 = readl_relaxed(controller_id_addr + offset);
+ r2 = readl_relaxed(controller_id_addr + (offset +
+ TSENS_DEBUG_OFFSET_WORD1));
+ r3 = readl_relaxed(controller_id_addr + (offset +
+ TSENS_DEBUG_OFFSET_WORD2));
+ r4 = readl_relaxed(controller_id_addr + (offset +
+ TSENS_DEBUG_OFFSET_WORD3));
+
+ pr_err("ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ cntrl_id, offset, r1, r2, r3, r4);
+ offset += TSENS_DEBUG_OFFSET_ROW;
+ }
+
+ offset = 0;
+ pr_err("Start of TSENS SROT dump\n");
+ for (i = 0; i < TSENS_DEBUG_OFFSET_RANGE; i++) {
+ r1 = readl_relaxed(srot_addr + offset);
+ r2 = readl_relaxed(srot_addr + (offset +
+ TSENS_DEBUG_OFFSET_WORD1));
+ r3 = readl_relaxed(srot_addr + (offset +
+ TSENS_DEBUG_OFFSET_WORD2));
+ r4 = readl_relaxed(srot_addr + (offset +
+ TSENS_DEBUG_OFFSET_WORD3));
+
+ pr_err("ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ cntrl_id, offset, r1, r2, r3, r4);
+ offset += TSENS_DEBUG_OFFSET_ROW;
+ }
+
+ loop = 0;
+ while (loop < TSENS_DEBUG_LOOP_COUNT) {
+ offset = TSENS_DEBUG_OFFSET_ROW *
+ TSENS_DEBUG_STATUS_REG_START;
+ pr_err("Start of TSENS TM dump %d\n", loop);
+ /* Limited dump of the registers for the temperature */
+ for (i = 0; i < TSENS_DEBUG_LOOP_COUNT; i++) {
+ r1 = readl_relaxed(controller_id_addr + offset);
+ r2 = readl_relaxed(controller_id_addr +
+ (offset + TSENS_DEBUG_OFFSET_WORD1));
+ r3 = readl_relaxed(controller_id_addr +
+ (offset + TSENS_DEBUG_OFFSET_WORD2));
+ r4 = readl_relaxed(controller_id_addr +
+ (offset + TSENS_DEBUG_OFFSET_WORD3));
+
+ pr_err("ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ cntrl_id, offset, r1, r2, r3, r4);
+ offset += TSENS_DEBUG_OFFSET_ROW;
+ }
+ loop++;
+ msleep(TSENS_DEBUG_CYCLE_MS);
+ }
+ BUG();
+ }
+
+re_schedule:
+
+ schedule_delayed_work(&tmdev->tsens_critical_poll_test,
+ msecs_to_jiffies(tsens_sec_to_msec_value));
+}
+
+int tsens_mtc_reset_history_counter(unsigned int zone)
+{
+ unsigned int reg_cntl, is_valid;
+ void __iomem *sensor_addr;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
+ return -EINVAL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR(tmdev->tsens_addr);
+ reg_cntl = readl_relaxed((sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ is_valid = (reg_cntl & TSENS_RESET_HISTORY_MASK)
+ >> TSENS_RESET_HISTORY_SHIFT;
+ if (!is_valid) {
+ /*Enable the bit to reset counter*/
+ writel_relaxed(reg_cntl | (1 << TSENS_RESET_HISTORY_SHIFT),
+ (sensor_addr + (zone * TSENS_SN_ADDR_OFFSET)));
+ reg_cntl = readl_relaxed((sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ pr_debug("tsens : zone =%d reg=%x\n", zone , reg_cntl);
+ }
+
+ /*Disble the bit to start counter*/
+ writel_relaxed(reg_cntl & ~(1 << TSENS_RESET_HISTORY_SHIFT),
+ (sensor_addr + (zone * TSENS_SN_ADDR_OFFSET)));
+ reg_cntl = readl_relaxed((sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ pr_debug("tsens : zone =%d reg=%x\n", zone , reg_cntl);
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_mtc_reset_history_counter);
+
+int tsens_set_mtc_zone_sw_mask(unsigned int zone , unsigned int th1_enable,
+ unsigned int th2_enable)
+{
+ unsigned int reg_cntl;
+ void __iomem *sensor_addr;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
+ return -EINVAL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (tmdev->tsens_type == TSENS_TYPE3)
+ sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR
+ (tmdev->tsens_addr);
+ else
+ sensor_addr = TSENS_MTC_ZONE0_SW_MASK_ADDR
+ (tmdev->tsens_addr);
+
+ if (th1_enable && th2_enable)
+ writel_relaxed(TSENS_MTC_IN_EFFECT,
+ (sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ if (!th1_enable && !th2_enable)
+ writel_relaxed(TSENS_MTC_DISABLE,
+ (sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ if (th1_enable && !th2_enable)
+ writel_relaxed(TSENS_TH1_MTC_IN_EFFECT,
+ (sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ if (!th1_enable && th2_enable)
+ writel_relaxed(TSENS_TH2_MTC_IN_EFFECT,
+ (sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ reg_cntl = readl_relaxed((sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ pr_debug("tsens : zone =%d th1=%d th2=%d reg=%x\n",
+ zone , th1_enable , th2_enable , reg_cntl);
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_set_mtc_zone_sw_mask);
+
+int tsens_get_mtc_zone_log(unsigned int zone , void *zone_log)
+{
+ unsigned int i , reg_cntl , is_valid , log[TSENS_MTC_ZONE_LOG_SIZE];
+ int *zlog = (int *)zone_log;
+ void __iomem *sensor_addr;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
+ return -EINVAL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (tmdev->tsens_type == TSENS_TYPE3)
+ sensor_addr = TSENS_TM_MTC_ZONE0_LOG(tmdev->tsens_addr);
+ else
+ sensor_addr = TSENS_MTC_ZONE0_LOG(tmdev->tsens_addr);
+
+ reg_cntl = readl_relaxed((sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+ is_valid = (reg_cntl & TSENS_LOGS_VALID_MASK)
+ >> TSENS_LOGS_VALID_SHIFT;
+ if (is_valid) {
+ log[0] = (reg_cntl & TSENS_LOGS_LATEST_MASK);
+ log[1] = (reg_cntl & TSENS_LOGS_LOG1_MASK)
+ >> TSENS_LOGS_LOG1_SHIFT;
+ log[2] = (reg_cntl & TSENS_LOGS_LOG2_MASK)
+ >> TSENS_LOGS_LOG2_SHIFT;
+ log[3] = (reg_cntl & TSENS_LOGS_LOG3_MASK)
+ >> TSENS_LOGS_LOG3_SHIFT;
+ log[4] = (reg_cntl & TSENS_LOGS_LOG4_MASK)
+ >> TSENS_LOGS_LOG4_SHIFT;
+ log[5] = (reg_cntl & TSENS_LOGS_LOG5_MASK)
+ >> TSENS_LOGS_LOG5_SHIFT;
+ for (i = 0; i < (TSENS_MTC_ZONE_LOG_SIZE); i++) {
+ *(zlog+i) = log[i];
+ pr_debug("Log[%d]=%d\n", i , log[i]);
+ }
+ } else {
+ pr_debug("tsens: Valid bit disabled\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_mtc_zone_log);
+
+int tsens_get_mtc_zone_history(unsigned int zone , void *zone_hist)
+{
+ unsigned int i, reg_cntl, hist[TSENS_MTC_ZONE_HISTORY_SIZE];
+ int *zhist = (int *)zone_hist;
+ void __iomem *sensor_addr;
+ struct tsens_tm_device *tmdev = NULL;
+
+ if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
+ return -EINVAL;
+
+ tmdev = tsens_controller_is_present();
+ if (!tmdev) {
+ pr_err("No TSENS controller present\n");
+ return -EPROBE_DEFER;
+ }
+
+ sensor_addr = TSENS_TM_MTC_ZONE0_HISTORY(tmdev->tsens_addr);
+ reg_cntl = readl_relaxed((sensor_addr +
+ (zone * TSENS_SN_ADDR_OFFSET)));
+
+ hist[0] = (reg_cntl & TSENS_PS_COOL_CMD_MASK);
+ hist[1] = (reg_cntl & TSENS_PS_YELLOW_CMD_MASK)
+ >> TSENS_PS_YELLOW_CMD_SHIFT;
+ hist[2] = (reg_cntl & TSENS_PS_RED_CMD_MASK)
+ >> TSENS_PS_RED_CMD_SHIFT;
+ for (i = 0; i < (TSENS_MTC_ZONE_HISTORY_SIZE); i++) {
+ *(zhist+i) = hist[i];
+ pr_debug("tsens : %d\n", hist[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tsens_get_mtc_zone_history);
+
+static struct thermal_zone_device_ops tsens_thermal_zone_ops = {
+ .get_temp = tsens_tz_get_temp,
+ .get_mode = tsens_tz_get_mode,
+ .get_trip_type = tsens_tz_get_trip_type,
+ .activate_trip_type = tsens_tz_activate_trip_type,
+ .get_trip_temp = tsens_tz_get_trip_temp,
+ .set_trip_temp = tsens_tz_set_trip_temp,
+ .notify = tsens_tz_notify,
+};
+
+/* Thermal zone ops for decidegC */
+static struct thermal_zone_device_ops tsens_tm_thermal_zone_ops = {
+ .get_temp = tsens_tz_get_temp,
+ .get_trip_type = tsens_tm_get_trip_type,
+ .activate_trip_type = tsens_tm_activate_trip_type,
+ .get_trip_temp = tsens_tm_get_trip_temp,
+ .set_trip_temp = tsens_tm_set_trip_temp,
+ .notify = tsens_tz_notify,
+};
+
+static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
+{
+ struct tsens_tm_device *tm = data;
+ unsigned int i, status, idx = 0;
+ unsigned long flags;
+ void __iomem *sensor_status_addr;
+ void __iomem *sensor_int_mask_addr;
+ void __iomem *sensor_critical_addr;
+ void __iomem *wd_critical_addr;
+ int sensor_sw_id = -EINVAL, rc = 0;
+ int wd_mask;
+
+ tm->crit_set = false;
+ sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_addr);
+ sensor_int_mask_addr =
+ TSENS_TM_CRITICAL_INT_MASK(tm->tsens_addr);
+ sensor_critical_addr =
+ TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_addr);
+ wd_critical_addr =
+ TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_addr);
+
+ if (tm->wd_bark) {
+ wd_mask = readl_relaxed(wd_critical_addr);
+ /*
+ * Check whether the reason for critical interrupt is
+ * because of watchdog
+ */
+ if (wd_mask & TSENS_TM_CRITICAL_WD_BARK) {
+ /*
+ * Clear watchdog interrupt and
+ * increment global wd count
+ */
+ writel_relaxed(wd_mask | TSENS_TM_CRITICAL_WD_BARK,
+ (TSENS_TM_CRITICAL_INT_CLEAR
+ (tm->tsens_addr)));
+ writel_relaxed(wd_mask & ~(TSENS_TM_CRITICAL_WD_BARK),
+ (TSENS_TM_CRITICAL_INT_CLEAR
+ (tm->tsens_addr)));
+ tm->tsens_critical_wd_cnt++;
+ return IRQ_HANDLED;
+ }
+ }
+
+ for (i = 0; i < tm->tsens_num_sensor; i++) {
+ bool critical_thr = false;
+ int int_mask, int_mask_val;
+ uint32_t addr_offset;
+
+ spin_lock_irqsave(&tm->tsens_crit_lock, flags);
+ addr_offset = tm->sensor[i].sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET;
+ status = readl_relaxed(sensor_status_addr + addr_offset);
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+
+ if ((status & TSENS_TM_SN_STATUS_CRITICAL_STATUS) &&
+ !(int_mask & (1 << tm->sensor[i].sensor_hw_num))) {
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+ int_mask_val = (1 << tm->sensor[i].sensor_hw_num);
+ /* Mask the corresponding interrupt for the sensors */
+ writel_relaxed(int_mask | int_mask_val,
+ TSENS_TM_CRITICAL_INT_MASK(
+ tm->tsens_addr));
+ /* Clear the corresponding sensors interrupt */
+ writel_relaxed(int_mask_val,
+ TSENS_TM_CRITICAL_INT_CLEAR(tm->tsens_addr));
+ writel_relaxed(0,
+ TSENS_TM_CRITICAL_INT_CLEAR(
+ tm->tsens_addr));
+ critical_thr = true;
+ tm->sensor[i].debug_thr_state_copy.
+ crit_th_state = THERMAL_DEVICE_DISABLED;
+ }
+ spin_unlock_irqrestore(&tm->tsens_crit_lock, flags);
+
+ if (critical_thr) {
+ int temp;
+
+ tsens_tz_get_temp(tm->sensor[i].tz_dev, &temp);
+ rc = tsens_get_sw_id_mapping_for_controller(
+ tm->sensor[i].sensor_hw_num,
+ &sensor_sw_id, tm);
+ if (rc < 0)
+ pr_err("tsens mapping index not found\n");
+ pr_debug("sensor:%d trigger temp (%d degC) with count:%d\n",
+ tm->sensor[i].sensor_hw_num,
+ (status & TSENS_TM_SN_LAST_TEMP_MASK),
+ tm->tsens_critical_irq_cnt);
+ tm->tsens_critical_irq_cnt++;
+ }
+ }
+
+ idx = tm->crit_timestamp_last_interrupt_handled.idx;
+ tm->crit_timestamp_last_interrupt_handled.dbg_count[idx%10]++;
+ tm->crit_timestamp_last_interrupt_handled.time_stmp[idx%10] =
+ sched_clock();
+ tm->qtimer_val_last_detection_interrupt = arch_counter_get_cntvct();
+ if (tsens_poll_check)
+ complete(&tm->tsens_rslt_completion);
+ /* Mask critical interrupt */
+ mb();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsens_tm_irq_thread(int irq, void *data)
+{
+ struct tsens_tm_device *tm = data;
+ unsigned int i, status, threshold;
+ unsigned long flags;
+ void __iomem *sensor_status_addr;
+ void __iomem *sensor_int_mask_addr;
+ void __iomem *sensor_upper_lower_addr;
+ int sensor_sw_id = -EINVAL, rc = 0;
+ uint32_t addr_offset;
+
+ sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_addr);
+ sensor_int_mask_addr =
+ TSENS_TM_UPPER_LOWER_INT_MASK(tm->tsens_addr);
+ sensor_upper_lower_addr =
+ TSENS_TM_UPPER_LOWER_THRESHOLD(tm->tsens_addr);
+
+ for (i = 0; i < tm->tsens_num_sensor; i++) {
+ bool upper_thr = false, lower_thr = false;
+ int int_mask, int_mask_val = 0;
+
+ spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
+ addr_offset = tm->sensor[i].sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET;
+ status = readl_relaxed(sensor_status_addr + addr_offset);
+ threshold = readl_relaxed(sensor_upper_lower_addr +
+ addr_offset);
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+
+ if ((status & TSENS_TM_SN_STATUS_UPPER_STATUS) &&
+ !(int_mask &
+ (1 << (tm->sensor[i].sensor_hw_num + 16)))) {
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+ int_mask_val = TSENS_TM_UPPER_INT_SET(
+ tm->sensor[i].sensor_hw_num);
+ /* Mask the corresponding interrupt for the sensors */
+ writel_relaxed(int_mask | int_mask_val,
+ TSENS_TM_UPPER_LOWER_INT_MASK(
+ tm->tsens_addr));
+ /* Clear the corresponding sensors interrupt */
+ writel_relaxed(int_mask_val,
+ TSENS_TM_UPPER_LOWER_INT_CLEAR(
+ tm->tsens_addr));
+ writel_relaxed(0,
+ TSENS_TM_UPPER_LOWER_INT_CLEAR(
+ tm->tsens_addr));
+ upper_thr = true;
+ tm->sensor[i].debug_thr_state_copy.
+ high_th_state = THERMAL_DEVICE_DISABLED;
+ }
+
+ if ((status & TSENS_TM_SN_STATUS_LOWER_STATUS) &&
+ !(int_mask &
+ (1 << tm->sensor[i].sensor_hw_num))) {
+ int_mask = readl_relaxed(sensor_int_mask_addr);
+ int_mask_val = (1 << tm->sensor[i].sensor_hw_num);
+ /* Mask the corresponding interrupt for the sensors */
+ writel_relaxed(int_mask | int_mask_val,
+ TSENS_TM_UPPER_LOWER_INT_MASK(
+ tm->tsens_addr));
+ /* Clear the corresponding sensors interrupt */
+ writel_relaxed(int_mask_val,
+ TSENS_TM_UPPER_LOWER_INT_CLEAR(
+ tm->tsens_addr));
+ writel_relaxed(0,
+ TSENS_TM_UPPER_LOWER_INT_CLEAR(
+ tm->tsens_addr));
+ lower_thr = true;
+ tm->sensor[i].debug_thr_state_copy.
+ low_th_state = THERMAL_DEVICE_DISABLED;
+ }
+ spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
+
+ if (upper_thr || lower_thr) {
+ int temp;
+ enum thermal_trip_type trip =
+ THERMAL_TRIP_CONFIGURABLE_LOW;
+
+ if (upper_thr)
+ trip = THERMAL_TRIP_CONFIGURABLE_HI;
+ tsens_tz_get_temp(tm->sensor[i].tz_dev, &temp);
+ thermal_sensor_trip(tm->sensor[i].tz_dev, trip, temp);
+
+ rc = tsens_get_sw_id_mapping_for_controller(
+ tm->sensor[i].sensor_hw_num,
+ &sensor_sw_id, tm);
+ if (rc < 0)
+ pr_debug("tsens mapping index not found\n");
+ /* Use sensor_client_id for multiple controllers */
+ pr_debug("sensor:%d trigger temp (%d degC)\n",
+ tm->sensor[i].sensor_client_id,
+ (status & TSENS_TM_SN_LAST_TEMP_MASK));
+ if (upper_thr) {
+ trace_tsens_threshold_hit(
+ TSENS_TM_UPPER_THRESHOLD_VALUE(
+ threshold),
+ tm->sensor[i].sensor_client_id);
+ tm->tsens_upper_irq_cnt++;
+ } else {
+ trace_tsens_threshold_clear(
+ TSENS_TM_LOWER_THRESHOLD_VALUE(
+ threshold),
+ tm->sensor[i].sensor_client_id);
+ tm->tsens_lower_irq_cnt++;
+ }
+ }
+ }
+
+ /* Disable monitoring sensor trip threshold for triggered sensor */
+ mb();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsens_irq_thread(int irq, void *data)
+{
+ struct tsens_tm_device *tm = data;
+ unsigned int i, status, threshold;
+ void __iomem *sensor_status_addr;
+ void __iomem *sensor_status_ctrl_addr;
+ int sensor_sw_id = -EINVAL;
+ uint32_t idx = 0;
+
+ if ((tm->tsens_type == TSENS_TYPE2) ||
+ (tm->tsens_type == TSENS_TYPE4))
+ sensor_status_addr = TSENS2_SN_STATUS_ADDR(tm->tsens_addr);
+ else
+ sensor_status_addr = TSENS_S0_STATUS_ADDR(tm->tsens_addr);
+
+ sensor_status_ctrl_addr =
+ TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tm->tsens_addr);
+ for (i = 0; i < tm->tsens_num_sensor; i++) {
+ bool upper_thr = false, lower_thr = false;
+ uint32_t addr_offset;
+
+ sensor_sw_id = tm->sensor[i].sensor_sw_id;
+ addr_offset = tm->sensor[i].sensor_hw_num *
+ TSENS_SN_ADDR_OFFSET;
+ status = readl_relaxed(sensor_status_addr + addr_offset);
+ threshold = readl_relaxed(sensor_status_ctrl_addr +
+ addr_offset);
+ if (status & TSENS_SN_STATUS_UPPER_STATUS) {
+ writel_relaxed(threshold | TSENS_UPPER_STATUS_CLR,
+ TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
+ tm->tsens_addr + addr_offset));
+ upper_thr = true;
+ tm->sensor[i].debug_thr_state_copy.
+ high_th_state = THERMAL_DEVICE_DISABLED;
+ }
+ if (status & TSENS_SN_STATUS_LOWER_STATUS) {
+ writel_relaxed(threshold | TSENS_LOWER_STATUS_CLR,
+ TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
+ tm->tsens_addr + addr_offset));
+ lower_thr = true;
+ tm->sensor[i].debug_thr_state_copy.
+ low_th_state = THERMAL_DEVICE_DISABLED;
+ }
+ if (upper_thr || lower_thr) {
+ int temp;
+ enum thermal_trip_type trip =
+ THERMAL_TRIP_CONFIGURABLE_LOW;
+
+ if (upper_thr)
+ trip = THERMAL_TRIP_CONFIGURABLE_HI;
+ tsens_tz_get_temp(tm->sensor[i].tz_dev, &temp);
+ thermal_sensor_trip(tm->sensor[i].tz_dev, trip, temp);
+
+ pr_debug("sensor:%d trigger temp (%d degC)\n",
+ tm->sensor[i].sensor_hw_num,
+ tsens_tz_code_to_degc((status &
+ TSENS_SN_STATUS_TEMP_MASK),
+ tm->sensor[i].sensor_sw_id, tm));
+ if (upper_thr)
+ trace_tsens_threshold_hit(
+ tsens_tz_code_to_degc((threshold &
+ TSENS_UPPER_THRESHOLD_MASK) >>
+ TSENS_UPPER_THRESHOLD_SHIFT,
+ sensor_sw_id, tm),
+ tm->sensor[i].sensor_hw_num);
+ else
+ trace_tsens_threshold_clear(
+ tsens_tz_code_to_degc((threshold &
+ TSENS_LOWER_THRESHOLD_MASK),
+ sensor_sw_id, tm),
+ tm->sensor[i].sensor_hw_num);
+ }
+ }
+ /* debug */
+ idx = tm->tsens_thread_iq_dbg.idx;
+ tm->tsens_thread_iq_dbg.dbg_count[idx%10]++;
+ tm->tsens_thread_iq_dbg.time_stmp[idx%10] = sched_clock();
+ tm->tsens_thread_iq_dbg.idx++;
+
+ /* Disable monitoring sensor trip threshold for triggered sensor */
+ mb();
+
+ return IRQ_HANDLED;
+}
+
+static int tsens_hw_init(struct tsens_tm_device *tmdev)
+{
+ void __iomem *srot_addr;
+ void __iomem *sensor_int_mask_addr;
+ unsigned int srot_val;
+ int crit_mask;
+
+ if (!tmdev) {
+ pr_err("Invalid tsens device\n");
+ return -EINVAL;
+ }
+
+ if (tmdev->tsens_type == TSENS_TYPE3) {
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_addr + 0x4);
+ srot_val = readl_relaxed(srot_addr);
+ if (!(srot_val & TSENS_EN)) {
+ pr_err("TSENS device is not enabled\n");
+ return -ENODEV;
+ }
+
+ if (tmdev->cycle_compltn_monitor) {
+ sensor_int_mask_addr =
+ TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_addr);
+ crit_mask = readl_relaxed(sensor_int_mask_addr);
+ writel_relaxed(
+ crit_mask | tmdev->cycle_compltn_monitor_val,
+ (TSENS_TM_CRITICAL_INT_MASK
+ (tmdev->tsens_addr)));
+ /*Update critical cycle monitoring*/
+ mb();
+ }
+ writel_relaxed(TSENS_TM_CRITICAL_INT_EN |
+ TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN,
+ TSENS_TM_INT_EN(tmdev->tsens_addr));
+ } else
+ writel_relaxed(TSENS_INTERRUPT_EN,
+ TSENS_UPPER_LOWER_INTERRUPT_CTRL(tmdev->tsens_addr));
+
+ return 0;
+}
+
+static int get_device_tree_data(struct platform_device *pdev,
+ struct tsens_tm_device *tmdev)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ struct resource *res_mem = NULL;
+ u32 *tsens_slope_data = NULL, *sensor_id, *client_id;
+ u32 *temp1_calib_offset_factor, *temp2_calib_offset_factor;
+ u32 rc = 0, i, tsens_num_sensors = 0;
+ u32 cycle_monitor = 0, wd_bark = 0;
+ const struct of_device_id *id;
+
+ rc = of_property_read_u32(of_node,
+ "qcom,sensors", &tsens_num_sensors);
+ if (rc) {
+ dev_err(&pdev->dev, "missing sensor number\n");
+ return -ENODEV;
+ }
+
+ if (tsens_num_sensors == 0) {
+ pr_err("No sensors?\n");
+ return -ENODEV;
+ }
+
+ /* TSENS calibration region */
+ tmdev->res_calib_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tsens_eeprom_physical");
+ if (!tmdev->res_calib_mem) {
+ pr_debug("Using controller programmed gain and offset\n");
+ tmdev->gain_offset_programmed = true;
+ } else {
+ tsens_slope_data = devm_kzalloc(&pdev->dev,
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
+ if (!tsens_slope_data)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(of_node,
+ "qcom,slope", tsens_slope_data, tsens_num_sensors);
+ if (rc) {
+ dev_err(&pdev->dev, "missing property: tsens-slope\n");
+ return rc;
+ };
+ }
+
+ if (!of_match_node(tsens_match, of_node)) {
+ pr_err("Need to read SoC specific fuse map\n");
+ return -ENODEV;
+ }
+
+ id = of_match_node(tsens_match, of_node);
+ if (id == NULL) {
+ pr_err("can not find tsens_match of_node\n");
+ return -ENODEV;
+ }
+
+ if (!tmdev->gain_offset_programmed) {
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].slope_mul_tsens_factor =
+ tsens_slope_data[i];
+ tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
+ }
+
+ tmdev->tsens_num_sensor = tsens_num_sensors;
+ tmdev->calibration_less_mode = of_property_read_bool(of_node,
+ "qcom,calibration-less-mode");
+ tmdev->tsens_local_init = of_property_read_bool(of_node,
+ "qcom,tsens-local-init");
+
+ sensor_id = devm_kzalloc(&pdev->dev,
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
+ if (!sensor_id)
+ return -ENOMEM;
+
+ client_id = devm_kzalloc(&pdev->dev,
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
+ if (!client_id)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(of_node,
+ "qcom,client-id", client_id, tsens_num_sensors);
+ if (rc) {
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].sensor_client_id = i;
+ pr_debug("Default client id mapping\n");
+ } else {
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].sensor_client_id = client_id[i];
+ pr_debug("Use specified client id mapping\n");
+ }
+
+ rc = of_property_read_u32_array(of_node,
+ "qcom,sensor-id", sensor_id, tsens_num_sensors);
+ if (rc) {
+ pr_debug("Default sensor id mapping\n");
+ for (i = 0; i < tsens_num_sensors; i++) {
+ tmdev->sensor[i].sensor_hw_num = i;
+ tmdev->sensor[i].sensor_sw_id = i;
+ }
+ } else {
+ pr_debug("Use specified sensor id mapping\n");
+ for (i = 0; i < tsens_num_sensors; i++) {
+ tmdev->sensor[i].sensor_hw_num = sensor_id[i];
+ tmdev->sensor[i].sensor_sw_id = i;
+ }
+ }
+
+ rc = of_property_read_u32(of_node,
+ "qcom,cycle-monitor", &cycle_monitor);
+ if (rc) {
+ pr_debug("Default cycle completion monitor\n");
+ tmdev->cycle_compltn_monitor = false;
+ } else {
+ pr_debug("Use specified cycle completion monitor\n");
+ tmdev->cycle_compltn_monitor = true;
+ tmdev->cycle_compltn_monitor_val = cycle_monitor;
+ }
+
+ rc = of_property_read_u32(of_node,
+ "qcom,wd-bark", &wd_bark);
+ if (rc) {
+ pr_debug("Default Watchdog bark\n");
+ tmdev->wd_bark = false;
+ } else {
+ pr_debug("Use specified Watchdog bark\n");
+ tmdev->wd_bark = true;
+ tmdev->wd_bark_val = wd_bark;
+ }
+
+ if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
+ (!strcmp(id->compatible, "qcom,msm8998-tsens")))
+ tmdev->tsens_type = TSENS_TYPE3;
+ else if (!strcmp(id->compatible, "qcom,msmtitanium-tsens") ||
+ (!strcmp(id->compatible, "qcom,sdm660-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm630-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens"))) {
+ tmdev->tsens_type = TSENS_TYPE3;
+ tsens_poll_check = 0;
+ } else
+ tmdev->tsens_type = TSENS_TYPE0;
+
+ tmdev->tsens_valid_status_check = of_property_read_bool(of_node,
+ "qcom,valid-status-check");
+ if (!tmdev->tsens_valid_status_check) {
+ if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
+ (!strcmp(id->compatible, "qcom,msmtitanium-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msm8998-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm660-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm630-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens")))
+ tmdev->tsens_valid_status_check = true;
+ }
+
+ tmdev->tsens_irq = platform_get_irq_byname(pdev,
+ "tsens-upper-lower");
+ if (tmdev->tsens_irq < 0) {
+ pr_err("Invalid Upper/Lower get irq\n");
+ rc = tmdev->tsens_irq;
+ goto fail_tmdev;
+ }
+
+ if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
+ (!strcmp(id->compatible, "qcom,msm8998-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm660-tsens")) ||
+ (!strcmp(id->compatible, "qcom,sdm630-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmtitanium-tsens"))) {
+ tmdev->tsens_critical_irq =
+ platform_get_irq_byname(pdev,
+ "tsens-critical");
+ if (tmdev->tsens_critical_irq < 0) {
+ pr_err("Invalid Critical get irq\n");
+ rc = tmdev->tsens_critical_irq;
+ goto fail_tmdev;
+ }
+ }
+
+ temp1_calib_offset_factor = devm_kzalloc(&pdev->dev,
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
+ if (!temp1_calib_offset_factor)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(of_node,
+ "qcom,temp1-offset", temp1_calib_offset_factor,
+ tsens_num_sensors);
+ if (rc) {
+ pr_debug("Default temp1-offsets\n");
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].wa_temp1_calib_offset_factor = 0;
+ } else {
+ pr_debug("Use specific temp1-offsets\n");
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].wa_temp1_calib_offset_factor =
+ temp1_calib_offset_factor[i];
+ }
+
+ temp2_calib_offset_factor = devm_kzalloc(&pdev->dev,
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
+ if (!temp2_calib_offset_factor)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(of_node,
+ "qcom,temp2-offset", temp2_calib_offset_factor,
+ tsens_num_sensors);
+ if (rc) {
+ pr_debug("Default temp2-offsets\n");
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].wa_temp2_calib_offset_factor = 0;
+ } else {
+ pr_debug("Use specific temp2-offsets\n");
+ for (i = 0; i < tsens_num_sensors; i++)
+ tmdev->sensor[i].wa_temp2_calib_offset_factor =
+ temp2_calib_offset_factor[i];
+ }
+
+ /* TSENS register region */
+ tmdev->res_tsens_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tsens_physical");
+ if (!tmdev->res_tsens_mem) {
+ pr_err("Could not get tsens physical address resource\n");
+ rc = -EINVAL;
+ goto fail_tmdev;
+ }
+
+ tmdev->tsens_len = tmdev->res_tsens_mem->end -
+ tmdev->res_tsens_mem->start + 1;
+
+ res_mem = request_mem_region(tmdev->res_tsens_mem->start,
+ tmdev->tsens_len, tmdev->res_tsens_mem->name);
+ if (!res_mem) {
+ pr_err("Request tsens physical memory region failed\n");
+ rc = -EINVAL;
+ goto fail_tmdev;
+ }
+
+ tmdev->tsens_addr = ioremap(res_mem->start, tmdev->tsens_len);
+ if (!tmdev->tsens_addr) {
+ pr_err("Failed to IO map TSENS registers.\n");
+ rc = -EINVAL;
+ goto fail_unmap_tsens_region;
+ }
+
+ if (!tmdev->gain_offset_programmed) {
+ tmdev->calib_len = tmdev->res_calib_mem->end -
+ tmdev->res_calib_mem->start + 1;
+
+ tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start,
+ tmdev->calib_len);
+ if (!tmdev->tsens_calib_addr) {
+ pr_err("Failed to IO map EEPROM registers.\n");
+ rc = -EINVAL;
+ goto fail_unmap_tsens;
+ }
+ }
+
+ return 0;
+
+fail_unmap_tsens:
+ if (tmdev->tsens_addr)
+ iounmap(tmdev->tsens_addr);
+fail_unmap_tsens_region:
+ if (tmdev->res_tsens_mem)
+ release_mem_region(tmdev->res_tsens_mem->start,
+ tmdev->tsens_len);
+fail_tmdev:
+ platform_set_drvdata(pdev, NULL);
+
+ return rc;
+}
+
+static int tsens_tm_probe(struct platform_device *pdev)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ int rc, i;
+ u32 tsens_num_sensors;
+ struct tsens_tm_device *tmdev = NULL;
+
+ rc = of_property_read_u32(of_node,
+ "qcom,sensors", &tsens_num_sensors);
+ tmdev = devm_kzalloc(&pdev->dev,
+ sizeof(struct tsens_tm_device) +
+ tsens_num_sensors *
+ sizeof(struct tsens_tm_device_sensor),
+ GFP_KERNEL);
+ if (tmdev == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (pdev->dev.of_node) {
+ rc = get_device_tree_data(pdev, tmdev);
+ if (rc) {
+ pr_err("Error reading TSENS DT\n");
+ return rc;
+ }
+ } else
+ return -ENODEV;
+
+ tmdev->pdev = pdev;
+
+ tmdev->tsens_critical_wq = alloc_workqueue("tsens_critical_wq",
+ WQ_HIGHPRI, 0);
+ if (!tmdev->tsens_critical_wq) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ rc = tsens_hw_init(tmdev);
+ if (rc)
+ return rc;
+
+ tmdev->prev_reading_avail = true;
+
+ for (i = 0; i < 16; i++)
+ tmdev->sensor_dbg_info[i].idx = 0;
+
+ spin_lock_init(&tmdev->tsens_crit_lock);
+ spin_lock_init(&tmdev->tsens_upp_low_lock);
+ tmdev->is_ready = true;
+
+ list_add_tail(&tmdev->list, &tsens_device_list);
+ platform_set_drvdata(pdev, tmdev);
+
+ rc = create_tsens_mtc_sysfs(pdev);
+ if (rc < 0)
+ pr_debug("Cannot create create_tsens_mtc_sysfs %d\n", rc);
+
+ return 0;
+fail:
+ if (tmdev->tsens_critical_wq)
+ destroy_workqueue(tmdev->tsens_critical_wq);
+ if (tmdev->tsens_calib_addr)
+ iounmap(tmdev->tsens_calib_addr);
+ if (tmdev->tsens_addr)
+ iounmap(tmdev->tsens_addr);
+ if (tmdev->res_tsens_mem)
+ release_mem_region(tmdev->res_tsens_mem->start,
+ tmdev->tsens_len);
+ platform_set_drvdata(pdev, NULL);
+
+ return rc;
+}
+
+static ssize_t tsens_debugfs_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int nbytes = 0;
+ struct tsens_tm_device *tmdev = NULL;
+
+ list_for_each_entry(tmdev, &tsens_device_list, list) {
+ nbytes += scnprintf(dbg_buff + nbytes, 1024 - nbytes,
+ "TSENS Critical count: %d\n",
+ tmdev->tsens_critical_irq_cnt);
+ nbytes += scnprintf(dbg_buff + nbytes, 1024 - nbytes,
+ "TSENS Upper count: %d\n",
+ tmdev->tsens_upper_irq_cnt);
+ nbytes += scnprintf(dbg_buff + nbytes, 1024 - nbytes,
+ "TSENS Lower count: %d\n",
+ tmdev->tsens_lower_irq_cnt);
+
+ }
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+const struct file_operations tsens_stats_ops = {
+ .read = tsens_debugfs_read,
+};
+
+static void tsens_debugfs_init(void)
+{
+ const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
+
+ dent = debugfs_create_dir("tsens", 0);
+ if (IS_ERR(dent)) {
+ pr_err("Error creating TSENS directory\n");
+ return;
+ }
+
+ dfile_stats = debugfs_create_file("stats", read_only_mode, dent,
+ 0, &tsens_stats_ops);
+ if (!dfile_stats || IS_ERR(dfile_stats)) {
+ pr_err("Failed to create TSENS folder\n");
+ return;
+ }
+}
+
+static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev)
+{
+ int rc = 0, i = 0;
+
+ if (tmdev == NULL) {
+ pr_err("Invalid tsens instance\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+ char name[18];
+
+ snprintf(name, sizeof(name), "tsens_tz_sensor%d",
+ tmdev->sensor[i].sensor_client_id);
+ tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+ tmdev->sensor[i].tm = tmdev;
+ if (tmdev->tsens_type == TSENS_TYPE3) {
+ tmdev->sensor[i].tz_dev = thermal_zone_device_register(
+ name, TSENS_TRIP_NUM,
+ TSENS_WRITABLE_TRIPS_MASK,
+ &tmdev->sensor[i],
+ &tsens_tm_thermal_zone_ops, NULL, 0, 0);
+ if (IS_ERR(tmdev->sensor[i].tz_dev)) {
+ pr_err("%s: failed.\n", __func__);
+ rc = -ENODEV;
+ goto fail;
+ }
+ } else {
+ tmdev->sensor[i].tz_dev = thermal_zone_device_register(
+ name, TSENS_TRIP_NUM,
+ TSENS_WRITABLE_TRIPS_MASK,
+ &tmdev->sensor[i],
+ &tsens_thermal_zone_ops, NULL, 0, 0);
+ if (IS_ERR(tmdev->sensor[i].tz_dev)) {
+ pr_err("%s: failed.\n", __func__);
+ rc = -ENODEV;
+ goto fail;
+ }
+ }
+ }
+
+ if (tmdev->tsens_type == TSENS_TYPE3) {
+ rc = request_threaded_irq(tmdev->tsens_irq, NULL,
+ tsens_tm_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tsens_interrupt", tmdev);
+ if (rc < 0) {
+ pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+ for (i = 0; i < tmdev->tsens_num_sensor; i++)
+ thermal_zone_device_unregister(
+ tmdev->sensor[i].tz_dev);
+ goto fail;
+ } else {
+ enable_irq_wake(tmdev->tsens_irq);
+ }
+
+ rc = request_threaded_irq(tmdev->tsens_critical_irq, NULL,
+ tsens_tm_critical_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tsens_critical_interrupt", tmdev);
+ if (rc < 0) {
+ pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+ for (i = 0; i < tmdev->tsens_num_sensor; i++)
+ thermal_zone_device_unregister(
+ tmdev->sensor[i].tz_dev);
+ goto fail;
+ } else {
+ enable_irq_wake(tmdev->tsens_critical_irq);
+ }
+
+ if (tsens_poll_check) {
+ INIT_DEFERRABLE_WORK(&tmdev->tsens_critical_poll_test,
+ tsens_poll);
+ schedule_delayed_work(&tmdev->tsens_critical_poll_test,
+ msecs_to_jiffies(tsens_sec_to_msec_value));
+ init_completion(&tmdev->tsens_rslt_completion);
+ tmdev->tsens_critical_poll = true;
+ }
+ } else {
+ rc = request_threaded_irq(tmdev->tsens_irq, NULL,
+ tsens_irq_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tsens_interrupt", tmdev);
+ if (rc < 0) {
+ pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+ for (i = 0; i < tmdev->tsens_num_sensor; i++)
+ thermal_zone_device_unregister(
+ tmdev->sensor[i].tz_dev);
+ goto fail;
+ } else {
+ enable_irq_wake(tmdev->tsens_irq);
+ }
+ }
+
+ return 0;
+fail:
+ if (tmdev->tsens_calib_addr)
+ iounmap(tmdev->tsens_calib_addr);
+ if (tmdev->tsens_addr)
+ iounmap(tmdev->tsens_addr);
+ if (tmdev->res_tsens_mem)
+ release_mem_region(tmdev->res_tsens_mem->start,
+ tmdev->tsens_len);
+ return rc;
+}
+
+static int _tsens_register_thermal(void)
+{
+ struct tsens_tm_device *tmdev = NULL;
+ int rc;
+
+ if (tsens_is_ready() <= 0) {
+ pr_err("%s: TSENS early init not done\n", __func__);
+ return -ENODEV;
+ }
+
+ list_for_each_entry(tmdev, &tsens_device_list, list) {
+ rc = tsens_thermal_zone_register(tmdev);
+ if (rc) {
+ pr_err("Error registering the thermal zone\n");
+ return rc;
+ }
+ }
+
+ tsens_debugfs_init();
+
+ return 0;
+}
+
+static int tsens_tm_remove(struct platform_device *pdev)
+{
+ struct tsens_tm_device *tmdev = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < tmdev->tsens_num_sensor; i++)
+ thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
+ if (tmdev->tsens_calib_addr)
+ iounmap(tmdev->tsens_calib_addr);
+ if (tmdev->tsens_addr)
+ iounmap(tmdev->tsens_addr);
+ if (tmdev->res_tsens_mem)
+ release_mem_region(tmdev->res_tsens_mem->start,
+ tmdev->tsens_len);
+ if (tmdev->tsens_critical_wq)
+ destroy_workqueue(tmdev->tsens_critical_wq);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver tsens_tm_driver = {
+ .probe = tsens_tm_probe,
+ .remove = tsens_tm_remove,
+ .driver = {
+ .name = "msm-tsens",
+ .owner = THIS_MODULE,
+ .of_match_table = tsens_match,
+ },
+};
+
+int __init tsens_tm_init_driver(void)
+{
+ return platform_driver_register(&tsens_tm_driver);
+}
+arch_initcall(tsens_tm_init_driver);
+
+static int __init tsens_thermal_register(void)
+{
+ return _tsens_register_thermal();
+}
+module_init(tsens_thermal_register);
+
+static void __exit _tsens_tm_remove(void)
+{
+ platform_driver_unregister(&tsens_tm_driver);
+}
+module_exit(_tsens_tm_remove);
+
+MODULE_ALIAS("platform:" TSENS_DRIVER_NAME);
+MODULE_LICENSE("GPL v2");