| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/err.h> |
| #include <linux/of.h> |
| #include <linux/vmalloc.h> |
| #include "tsens.h" |
| #include "thermal_core.h" |
| #include <soc/qcom/scm.h> |
| |
| #define TSENS_DRIVER_NAME "msm-tsens" |
| |
| #define TSENS_TM_INT_EN(n) ((n) + 0x4) |
| #define TSENS_TM_CRITICAL_INT_STATUS(n) ((n) + 0x14) |
| #define TSENS_TM_CRITICAL_INT_CLEAR(n) ((n) + 0x18) |
| #define TSENS_TM_CRITICAL_INT_MASK(n) ((n) + 0x1c) |
| #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_LOWER_INT_DISABLE 0xffffffff |
| #define TSENS_TM_SN_UPPER_LOWER_THRESHOLD(n) ((n) + 0x20) |
| #define TSENS_TM_SN_ADDR_OFFSET 0x4 |
| #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(n) ((n) + 0x60) |
| #define TSENS_STATUS_ADDR_OFFSET 2 |
| #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) + 0x8) |
| #define TSENS_TM_UPPER_LOWER_INT_CLEAR(n) ((n) + 0xc) |
| #define TSENS_TM_UPPER_LOWER_INT_MASK(n) ((n) + 0x10) |
| #define TSENS_TM_UPPER_INT_SET(n) (1 << (n + 16)) |
| #define TSENS_TM_SN_CRITICAL_THRESHOLD_MASK 0xfff |
| #define TSENS_TM_MIN_TEMP_VALID_BIT BIT(16) |
| #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_CODE_BIT_MASK 0xfff |
| #define TSENS_TM_0C_THR_MASK 0xfff |
| #define TSENS_TM_0C_THR_OFFSET 12 |
| #define TSENS_TM_CODE_SIGN_BIT 0x800 |
| #define TSENS_TM_SCALE_DECI_MILLIDEG 100 |
| #define TSENS_DEBUG_WDOG_TRIGGER_COUNT 5 |
| #define TSENS_TM_WATCHDOG_LOG(n) ((n) + 0x13c) |
| #define TSENS_TM_WATCHDOG_LOG_v23(n) ((n) + 0x170) |
| #define TSENS_EN BIT(0) |
| #define TSENS_CTRL_SENSOR_EN_MASK(n) ((n >> 3) & 0xffff) |
| #define TSENS_TM_TRDY(n) ((n) + 0xe4) |
| #define TSENS_TM_TRDY_FIRST_ROUND_COMPLETE BIT(3) |
| #define TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT 3 |
| #define TSENS_TM_0C_INT_STATUS(n) ((n) + 0xe0) |
| #define TSENS_TM_MIN_TEMP(n) ((n) + 0xec) |
| #define TSENS_TM_0C_THRESHOLDS(n) ((n) + 0x1c) |
| #define TSENS_MAX_READ_FAIL 50 |
| |
| #define TSENS_INIT_ID 0x5 |
| #define TSENS_RECOVERY_LOOP_COUNT 5 |
| |
| static void msm_tsens_convert_temp(int last_temp, int *temp) |
| { |
| int code_mask = ~TSENS_TM_CODE_BIT_MASK; |
| |
| if (last_temp & TSENS_TM_CODE_SIGN_BIT) { |
| /* Sign extension for negative value */ |
| last_temp |= code_mask; |
| } |
| |
| *temp = last_temp * TSENS_TM_SCALE_DECI_MILLIDEG; |
| } |
| |
| static int __tsens2xxx_hw_init(struct tsens_device *tmdev) |
| { |
| void __iomem *srot_addr; |
| void __iomem *sensor_int_mask_addr; |
| unsigned int srot_val, crit_mask, crit_val; |
| void __iomem *int_mask_addr; |
| |
| srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_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->ctrl_data->cycle_monitor) { |
| sensor_int_mask_addr = |
| TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr); |
| crit_mask = readl_relaxed(sensor_int_mask_addr); |
| crit_val = TSENS_TM_CRITICAL_CYCLE_MONITOR; |
| if (tmdev->ctrl_data->cycle_compltn_monitor_mask) |
| writel_relaxed((crit_mask | crit_val), |
| (TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| else |
| writel_relaxed((crit_mask & ~crit_val), |
| (TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| /*Update critical cycle monitoring*/ |
| mb(); |
| } |
| |
| if (tmdev->ctrl_data->wd_bark) { |
| sensor_int_mask_addr = |
| TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr); |
| crit_mask = readl_relaxed(sensor_int_mask_addr); |
| crit_val = TSENS_TM_CRITICAL_WD_BARK; |
| if (tmdev->ctrl_data->wd_bark_mask) |
| writel_relaxed((crit_mask | crit_val), |
| (TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| else |
| writel_relaxed((crit_mask & ~crit_val), |
| (TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| /*Update watchdog monitoring*/ |
| mb(); |
| } |
| |
| int_mask_addr = TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_tm_addr); |
| writel_relaxed(TSENS_TM_UPPER_LOWER_INT_DISABLE, int_mask_addr); |
| |
| writel_relaxed(TSENS_TM_CRITICAL_INT_EN | |
| TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN, |
| TSENS_TM_INT_EN(tmdev->tsens_tm_addr)); |
| |
| return 0; |
| } |
| |
| static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp) |
| { |
| struct tsens_device *tmdev = NULL, *tmdev_itr; |
| unsigned int code, ret, tsens_ret; |
| void __iomem *sensor_addr, *trdy; |
| int rc = 0, last_temp = 0, last_temp2 = 0, last_temp3 = 0, count = 0; |
| static atomic_t in_tsens_reinit; |
| |
| if (!sensor) |
| return -EINVAL; |
| |
| tmdev = sensor->tmdev; |
| sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_tm_addr); |
| trdy = TSENS_TM_TRDY(tmdev->tsens_tm_addr); |
| |
| code = readl_relaxed_no_log(trdy); |
| |
| if (!((code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) >> |
| TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT)) { |
| if (atomic_read(&in_tsens_reinit)) { |
| pr_err("%s: tsens re-init is in progress\n", __func__); |
| return -EAGAIN; |
| } |
| |
| pr_err("%s: tsens device first round not complete0x%x\n", |
| __func__, code); |
| |
| /* Wait for 2.5 ms for tsens controller to recover */ |
| do { |
| udelay(500); |
| code = readl_relaxed_no_log(trdy); |
| if (code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) { |
| TSENS_DUMP(tmdev, "%s", |
| "tsens controller recovered\n"); |
| goto sensor_read; |
| } |
| } while (++count < TSENS_RECOVERY_LOOP_COUNT); |
| |
| /* |
| * TSENS controller did not recover, |
| * proceed with SCM call to re-init it |
| */ |
| if (tmdev->tsens_reinit_wa) { |
| struct scm_desc desc = { 0 }; |
| int scm_cnt = 0, reg_write_cnt = 0; |
| |
| if (atomic_read(&in_tsens_reinit)) { |
| pr_err("%s: tsens re-init is in progress\n", |
| __func__); |
| return -EAGAIN; |
| } |
| |
| atomic_set(&in_tsens_reinit, 1); |
| |
| if (tmdev->ops->dbg) |
| tmdev->ops->dbg(tmdev, 0, |
| TSENS_DBG_LOG_BUS_ID_DATA, NULL); |
| |
| while (1) { |
| /* |
| * Invoke scm call only if SW register write is |
| * reflecting in controller. If not, wait for |
| * 2 ms and then retry. |
| */ |
| if (reg_write_cnt >= 100) { |
| msleep(100); |
| pr_err( |
| "%s: Tsens write is failed. cnt:%d\n", |
| __func__, reg_write_cnt); |
| BUG(); |
| } |
| writel_relaxed(BIT(2), |
| TSENS_TM_INT_EN(tmdev->tsens_tm_addr)); |
| code = readl_relaxed( |
| TSENS_TM_INT_EN(tmdev->tsens_tm_addr)); |
| if (!(code & BIT(2))) { |
| udelay(2000); |
| TSENS_DBG(tmdev, "%s cnt:%d\n", |
| "Re-try TSENS write prior to scm", |
| reg_write_cnt++); |
| continue; |
| } |
| reg_write_cnt = 0; |
| |
| /* Make an scm call to re-init TSENS */ |
| TSENS_DBG(tmdev, "%s", |
| "Calling TZ to re-init TSENS\n"); |
| ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TSENS, |
| TSENS_INIT_ID), &desc); |
| TSENS_DBG(tmdev, "%s", |
| "return from scm call\n"); |
| if (ret) { |
| msleep(100); |
| pr_err("%s: scm call failed %d\n", |
| __func__, ret); |
| BUG(); |
| } |
| tsens_ret = desc.ret[0]; |
| if (tsens_ret) { |
| msleep(100); |
| pr_err("%s: scm call failed, ret:%d\n", |
| __func__, tsens_ret); |
| BUG(); |
| } |
| |
| scm_cnt++; |
| rc = 0; |
| list_for_each_entry(tmdev_itr, |
| &tsens_device_list, list) { |
| rc = __tsens2xxx_hw_init(tmdev_itr); |
| if (rc) { |
| pr_err( |
| "%s: TSENS hw_init error\n", |
| __func__); |
| break; |
| } |
| } |
| |
| if (!rc) |
| break; |
| |
| if (scm_cnt >= 100) { |
| msleep(100); |
| pr_err( |
| "%s: Tsens is not up after %d scm\n", |
| __func__, scm_cnt); |
| BUG(); |
| } |
| udelay(2000); |
| TSENS_DBG(tmdev, "%s cnt:%d\n", |
| "Re-try TSENS scm call", scm_cnt); |
| } |
| |
| tmdev->tsens_reinit_cnt++; |
| atomic_set(&in_tsens_reinit, 0); |
| |
| /* Notify thermal fwk */ |
| list_for_each_entry(tmdev_itr, |
| &tsens_device_list, list) { |
| queue_work(tmdev_itr->tsens_reinit_work, |
| &tmdev_itr->therm_fwk_notify); |
| } |
| |
| } else { |
| pr_err("%s: tsens controller got reset\n", __func__); |
| BUG(); |
| } |
| return -EAGAIN; |
| } |
| |
| sensor_read: |
| |
| tmdev->trdy_fail_ctr = 0; |
| |
| code = readl_relaxed_no_log(sensor_addr + |
| (sensor->hw_id << TSENS_STATUS_ADDR_OFFSET)); |
| last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK; |
| |
| if (code & TSENS_TM_SN_STATUS_VALID_BIT) { |
| msm_tsens_convert_temp(last_temp, temp); |
| goto dbg; |
| } |
| |
| code = readl_relaxed_no_log(sensor_addr + |
| (sensor->hw_id << TSENS_STATUS_ADDR_OFFSET)); |
| last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK; |
| if (code & TSENS_TM_SN_STATUS_VALID_BIT) { |
| last_temp = last_temp2; |
| msm_tsens_convert_temp(last_temp, temp); |
| goto dbg; |
| } |
| |
| code = readl_relaxed_no_log(sensor_addr + |
| (sensor->hw_id << |
| TSENS_STATUS_ADDR_OFFSET)); |
| last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK; |
| if (code & TSENS_TM_SN_STATUS_VALID_BIT) { |
| last_temp = last_temp3; |
| msm_tsens_convert_temp(last_temp, temp); |
| goto dbg; |
| } |
| |
| if (last_temp == last_temp2) |
| last_temp = last_temp2; |
| else if (last_temp2 == last_temp3) |
| last_temp = last_temp3; |
| |
| msm_tsens_convert_temp(last_temp, temp); |
| |
| dbg: |
| if (tmdev->ops->dbg) |
| tmdev->ops->dbg(tmdev, (u32) sensor->hw_id, |
| TSENS_DBG_LOG_TEMP_READS, temp); |
| |
| return 0; |
| } |
| |
| int tsens_2xxx_get_min_temp(struct tsens_sensor *sensor, int *temp) |
| { |
| struct tsens_device *tmdev = NULL; |
| unsigned int code; |
| void __iomem *sensor_addr, *trdy; |
| int last_temp = 0, last_temp2 = 0, last_temp3 = 0, valid_bit; |
| |
| if (!sensor) |
| return -EINVAL; |
| |
| tmdev = sensor->tmdev; |
| trdy = TSENS_TM_TRDY(tmdev->tsens_tm_addr); |
| |
| valid_bit = TSENS_TM_MIN_TEMP_VALID_BIT; |
| sensor_addr = TSENS_TM_MIN_TEMP(tmdev->tsens_tm_addr); |
| |
| code = readl_relaxed_no_log(trdy); |
| if (!((code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) >> |
| TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT)) { |
| pr_err("tsens device first round not complete0x%x, ctr is %d\n", |
| code, tmdev->trdy_fail_ctr); |
| tmdev->trdy_fail_ctr++; |
| if (tmdev->trdy_fail_ctr >= TSENS_MAX_READ_FAIL) { |
| if (tmdev->ops->dbg) |
| tmdev->ops->dbg(tmdev, 0, |
| TSENS_DBG_LOG_BUS_ID_DATA, NULL); |
| BUG(); |
| } |
| return -ENODATA; |
| } |
| |
| tmdev->trdy_fail_ctr = 0; |
| |
| code = readl_relaxed_no_log(sensor_addr); |
| last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK; |
| if (code & valid_bit) { |
| msm_tsens_convert_temp(last_temp, temp); |
| goto dbg; |
| } |
| |
| code = readl_relaxed_no_log(sensor_addr); |
| last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK; |
| if (code & valid_bit) { |
| last_temp = last_temp2; |
| msm_tsens_convert_temp(last_temp, temp); |
| goto dbg; |
| } |
| |
| code = readl_relaxed_no_log(sensor_addr); |
| last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK; |
| if (code & valid_bit) { |
| last_temp = last_temp3; |
| msm_tsens_convert_temp(last_temp, temp); |
| goto dbg; |
| } |
| |
| if (last_temp == last_temp2) |
| last_temp = last_temp2; |
| else if (last_temp2 == last_temp3) |
| last_temp = last_temp3; |
| |
| msm_tsens_convert_temp(last_temp, temp); |
| |
| dbg: |
| TSENS_DBG(tmdev, "Min temp: %d\n", *temp); |
| |
| return 0; |
| } |
| |
| static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor, |
| int trip, enum thermal_device_mode mode) |
| { |
| struct tsens_device *tmdev = NULL; |
| unsigned int reg_cntl, mask; |
| int rc = 0; |
| |
| /* clear the interrupt and unmask */ |
| if (!tm_sensor || trip < 0) |
| return -EINVAL; |
| |
| tmdev = tm_sensor->tmdev; |
| if (!tmdev) |
| return -EINVAL; |
| |
| |
| mask = (tm_sensor->hw_id); |
| switch (trip) { |
| case THERMAL_TRIP_CRITICAL: |
| tmdev->sensor[tm_sensor->hw_id].thr_state.crit_th_state = mode; |
| reg_cntl = readl_relaxed(TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr)); |
| if (mode == THERMAL_DEVICE_DISABLED) |
| writel_relaxed(reg_cntl | (1 << mask), |
| (TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| else |
| writel_relaxed(reg_cntl & ~(1 << mask), |
| (TSENS_TM_CRITICAL_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| break; |
| case THERMAL_TRIP_CONFIGURABLE_HI: |
| tmdev->sensor[tm_sensor->hw_id].thr_state.high_th_state = mode; |
| reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK |
| (tmdev->tsens_tm_addr)); |
| if (mode == THERMAL_DEVICE_DISABLED) |
| writel_relaxed(reg_cntl | |
| (TSENS_TM_UPPER_INT_SET(mask)), |
| (TSENS_TM_UPPER_LOWER_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| else |
| writel_relaxed(reg_cntl & |
| ~(TSENS_TM_UPPER_INT_SET(mask)), |
| (TSENS_TM_UPPER_LOWER_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| break; |
| case THERMAL_TRIP_CONFIGURABLE_LOW: |
| tmdev->sensor[tm_sensor->hw_id].thr_state.low_th_state = mode; |
| reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK |
| (tmdev->tsens_tm_addr)); |
| if (mode == THERMAL_DEVICE_DISABLED) |
| writel_relaxed(reg_cntl | (1 << mask), |
| (TSENS_TM_UPPER_LOWER_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| else |
| writel_relaxed(reg_cntl & ~(1 << mask), |
| (TSENS_TM_UPPER_LOWER_INT_MASK |
| (tmdev->tsens_tm_addr))); |
| break; |
| default: |
| rc = -EINVAL; |
| } |
| |
| /* Activate and enable the respective trip threshold setting */ |
| mb(); |
| |
| return rc; |
| } |
| |
| static int tsens2xxx_set_trip_temp(struct tsens_sensor *tm_sensor, |
| int low_temp, int high_temp) |
| { |
| unsigned int reg_cntl; |
| unsigned long flags; |
| struct tsens_device *tmdev = NULL; |
| int rc = 0; |
| |
| if (!tm_sensor) |
| return -EINVAL; |
| |
| tmdev = tm_sensor->tmdev; |
| if (!tmdev) |
| return -EINVAL; |
| |
| pr_debug("%s: sensor:%d low_temp(mdegC):%d, high_temp(mdegC):%d\n", |
| __func__, tm_sensor->hw_id, low_temp, high_temp); |
| |
| spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags); |
| |
| if (high_temp != INT_MAX) { |
| tmdev->sensor[tm_sensor->hw_id].thr_state.high_temp = high_temp; |
| reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD |
| (tmdev->tsens_tm_addr)) + |
| (tm_sensor->hw_id * |
| TSENS_TM_SN_ADDR_OFFSET)); |
| high_temp /= TSENS_TM_SCALE_DECI_MILLIDEG; |
| high_temp = TSENS_TM_UPPER_THRESHOLD_SET(high_temp); |
| high_temp &= TSENS_TM_UPPER_THRESHOLD_MASK; |
| reg_cntl &= ~TSENS_TM_UPPER_THRESHOLD_MASK; |
| writel_relaxed(reg_cntl | high_temp, |
| (TSENS_TM_SN_UPPER_LOWER_THRESHOLD |
| (tmdev->tsens_tm_addr) + |
| (tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET))); |
| } |
| |
| if (low_temp != INT_MIN) { |
| tmdev->sensor[tm_sensor->hw_id].thr_state.low_temp = low_temp; |
| reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD |
| (tmdev->tsens_tm_addr)) + |
| (tm_sensor->hw_id * |
| TSENS_TM_SN_ADDR_OFFSET)); |
| low_temp /= TSENS_TM_SCALE_DECI_MILLIDEG; |
| low_temp &= TSENS_TM_LOWER_THRESHOLD_MASK; |
| reg_cntl &= ~TSENS_TM_LOWER_THRESHOLD_MASK; |
| writel_relaxed(reg_cntl | low_temp, |
| (TSENS_TM_SN_UPPER_LOWER_THRESHOLD |
| (tmdev->tsens_tm_addr) + |
| (tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET))); |
| } |
| |
| /* Set trip temperature thresholds */ |
| mb(); |
| |
| if (high_temp != INT_MAX) { |
| rc = tsens_tm_activate_trip_type(tm_sensor, |
| THERMAL_TRIP_CONFIGURABLE_HI, |
| THERMAL_DEVICE_ENABLED); |
| if (rc) { |
| pr_err("trip high enable error :%d\n", rc); |
| goto fail; |
| } |
| } else { |
| rc = tsens_tm_activate_trip_type(tm_sensor, |
| THERMAL_TRIP_CONFIGURABLE_HI, |
| THERMAL_DEVICE_DISABLED); |
| if (rc) { |
| pr_err("trip high disable error :%d\n", rc); |
| goto fail; |
| } |
| } |
| |
| if (low_temp != INT_MIN) { |
| rc = tsens_tm_activate_trip_type(tm_sensor, |
| THERMAL_TRIP_CONFIGURABLE_LOW, |
| THERMAL_DEVICE_ENABLED); |
| if (rc) { |
| pr_err("trip low enable activation error :%d\n", rc); |
| goto fail; |
| } |
| } else { |
| rc = tsens_tm_activate_trip_type(tm_sensor, |
| THERMAL_TRIP_CONFIGURABLE_LOW, |
| THERMAL_DEVICE_DISABLED); |
| if (rc) { |
| pr_err("trip low disable error :%d\n", rc); |
| goto fail; |
| } |
| } |
| |
| fail: |
| spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags); |
| return rc; |
| } |
| |
| static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data) |
| { |
| struct tsens_device *tm = data; |
| unsigned int i, status, wd_log, wd_mask; |
| unsigned long flags; |
| void __iomem *sensor_status_addr, *sensor_int_mask_addr; |
| void __iomem *sensor_critical_addr; |
| void __iomem *wd_critical_addr, *wd_log_addr; |
| |
| sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr); |
| sensor_int_mask_addr = |
| TSENS_TM_CRITICAL_INT_MASK(tm->tsens_tm_addr); |
| sensor_critical_addr = |
| TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_tm_addr); |
| wd_critical_addr = |
| TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_tm_addr); |
| if (tm->ctrl_data->ver_major == 2 && tm->ctrl_data->ver_minor == 3) |
| wd_log_addr = TSENS_TM_WATCHDOG_LOG_v23(tm->tsens_tm_addr); |
| else |
| wd_log_addr = TSENS_TM_WATCHDOG_LOG(tm->tsens_tm_addr); |
| |
| if (tm->ctrl_data->wd_bark) { |
| wd_mask = readl_relaxed(wd_critical_addr); |
| 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_tm_addr))); |
| writel_relaxed(wd_mask & ~(TSENS_TM_CRITICAL_WD_BARK), |
| (TSENS_TM_CRITICAL_INT_CLEAR |
| (tm->tsens_tm_addr))); |
| wd_log = readl_relaxed(wd_log_addr); |
| if (wd_log >= TSENS_DEBUG_WDOG_TRIGGER_COUNT) { |
| pr_err("Watchdog count:%d\n", wd_log); |
| if (tm->ops->dbg) |
| tm->ops->dbg(tm, 0, |
| TSENS_DBG_LOG_BUS_ID_DATA, NULL); |
| BUG(); |
| } |
| |
| return IRQ_HANDLED; |
| } |
| } |
| |
| for (i = 0; i < TSENS_MAX_SENSORS; i++) { |
| int int_mask, int_mask_val; |
| u32 addr_offset; |
| |
| if (IS_ERR(tm->sensor[i].tzd)) |
| continue; |
| |
| spin_lock_irqsave(&tm->tsens_crit_lock, flags); |
| addr_offset = tm->sensor[i].hw_id * |
| TSENS_TM_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].hw_id))) { |
| int_mask = readl_relaxed(sensor_int_mask_addr); |
| int_mask_val = (1 << tm->sensor[i].hw_id); |
| /* Mask the corresponding interrupt for the sensors */ |
| writel_relaxed(int_mask | int_mask_val, |
| TSENS_TM_CRITICAL_INT_MASK( |
| tm->tsens_tm_addr)); |
| /* Clear the corresponding sensors interrupt */ |
| writel_relaxed(int_mask_val, |
| TSENS_TM_CRITICAL_INT_CLEAR |
| (tm->tsens_tm_addr)); |
| writel_relaxed(0, |
| TSENS_TM_CRITICAL_INT_CLEAR( |
| tm->tsens_tm_addr)); |
| tm->sensor[i].thr_state.crit_th_state = |
| THERMAL_DEVICE_DISABLED; |
| } |
| spin_unlock_irqrestore(&tm->tsens_crit_lock, flags); |
| } |
| |
| /* Mask critical interrupt */ |
| mb(); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t tsens_tm_irq_thread(int irq, void *data) |
| { |
| struct tsens_device *tm = data; |
| unsigned int i, status, threshold, temp; |
| unsigned long flags; |
| void __iomem *sensor_status_addr; |
| void __iomem *sensor_int_mask_addr; |
| void __iomem *sensor_upper_lower_addr; |
| u32 addr_offset = 0; |
| |
| sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr); |
| sensor_int_mask_addr = |
| TSENS_TM_UPPER_LOWER_INT_MASK(tm->tsens_tm_addr); |
| sensor_upper_lower_addr = |
| TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tm->tsens_tm_addr); |
| |
| for (i = 0; i < TSENS_MAX_SENSORS; i++) { |
| bool upper_thr = false, lower_thr = false; |
| int int_mask, int_mask_val = 0, rc; |
| |
| if (IS_ERR(tm->sensor[i].tzd)) |
| continue; |
| |
| rc = tsens2xxx_get_temp(&tm->sensor[i], &temp); |
| if (rc) { |
| pr_debug("Error:%d reading temp sensor:%d\n", rc, i); |
| continue; |
| } |
| |
| spin_lock_irqsave(&tm->tsens_upp_low_lock, flags); |
| addr_offset = tm->sensor[i].hw_id * |
| TSENS_TM_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].hw_id + 16)))) { |
| int_mask = readl_relaxed(sensor_int_mask_addr); |
| int_mask_val = TSENS_TM_UPPER_INT_SET( |
| tm->sensor[i].hw_id); |
| /* Mask the corresponding interrupt for the sensors */ |
| writel_relaxed(int_mask | int_mask_val, |
| TSENS_TM_UPPER_LOWER_INT_MASK( |
| tm->tsens_tm_addr)); |
| /* Clear the corresponding sensors interrupt */ |
| writel_relaxed(int_mask_val, |
| TSENS_TM_UPPER_LOWER_INT_CLEAR( |
| tm->tsens_tm_addr)); |
| writel_relaxed(0, |
| TSENS_TM_UPPER_LOWER_INT_CLEAR( |
| tm->tsens_tm_addr)); |
| if (TSENS_TM_UPPER_THRESHOLD_VALUE(threshold) > |
| (temp/TSENS_TM_SCALE_DECI_MILLIDEG)) { |
| pr_debug("Re-arm high threshold\n"); |
| rc = tsens_tm_activate_trip_type( |
| &tm->sensor[i], |
| THERMAL_TRIP_CONFIGURABLE_HI, |
| THERMAL_DEVICE_ENABLED); |
| if (rc) |
| pr_err("high rearm failed:%d\n", rc); |
| } else { |
| upper_thr = true; |
| tm->sensor[i].thr_state.high_th_state = |
| THERMAL_DEVICE_DISABLED; |
| } |
| } |
| |
| if ((status & TSENS_TM_SN_STATUS_LOWER_STATUS) && |
| !(int_mask & |
| (1 << tm->sensor[i].hw_id))) { |
| int_mask = readl_relaxed(sensor_int_mask_addr); |
| int_mask_val = (1 << tm->sensor[i].hw_id); |
| /* Mask the corresponding interrupt for the sensors */ |
| writel_relaxed(int_mask | int_mask_val, |
| TSENS_TM_UPPER_LOWER_INT_MASK( |
| tm->tsens_tm_addr)); |
| /* Clear the corresponding sensors interrupt */ |
| writel_relaxed(int_mask_val, |
| TSENS_TM_UPPER_LOWER_INT_CLEAR( |
| tm->tsens_tm_addr)); |
| writel_relaxed(0, |
| TSENS_TM_UPPER_LOWER_INT_CLEAR( |
| tm->tsens_tm_addr)); |
| if (TSENS_TM_LOWER_THRESHOLD_VALUE(threshold) |
| < (temp/TSENS_TM_SCALE_DECI_MILLIDEG)) { |
| pr_debug("Re-arm low threshold\n"); |
| rc = tsens_tm_activate_trip_type( |
| &tm->sensor[i], |
| THERMAL_TRIP_CONFIGURABLE_LOW, |
| THERMAL_DEVICE_ENABLED); |
| if (rc) |
| pr_err("low rearm failed:%d\n", rc); |
| } else { |
| lower_thr = true; |
| tm->sensor[i].thr_state.low_th_state = |
| THERMAL_DEVICE_DISABLED; |
| } |
| } |
| spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags); |
| |
| if (upper_thr || lower_thr) { |
| /* Use id for multiple controllers */ |
| pr_debug("sensor:%d trigger temp (%d degC)\n", |
| tm->sensor[i].hw_id, temp); |
| of_thermal_handle_trip_temp(tm->sensor[i].tzd, temp); |
| } |
| } |
| |
| /* Disable monitoring sensor trip threshold for triggered sensor */ |
| mb(); |
| |
| if (tm->ops->dbg) |
| tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t tsens_tm_0C_irq_thread(int irq, void *data) |
| { |
| struct tsens_device *tm = data; |
| int status, thrs, set_thr, reset_thr; |
| void __iomem *srot_addr, *addr; |
| |
| addr = TSENS_TM_0C_INT_STATUS(tm->tsens_tm_addr); |
| status = readl_relaxed(addr); |
| |
| srot_addr = TSENS_CTRL_ADDR(tm->tsens_srot_addr); |
| thrs = readl_relaxed(TSENS_TM_0C_THRESHOLDS(srot_addr)); |
| |
| msm_tsens_convert_temp(thrs & TSENS_TM_0C_THR_MASK, &reset_thr); |
| msm_tsens_convert_temp( |
| ((thrs >> TSENS_TM_0C_THR_OFFSET) & |
| TSENS_TM_0C_THR_MASK), &set_thr); |
| |
| if (status) |
| of_thermal_handle_trip_temp(tm->min_temp.tzd, set_thr); |
| else |
| of_thermal_handle_trip_temp(tm->min_temp.tzd, reset_thr); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int tsens2xxx_hw_sensor_en(struct tsens_device *tmdev, |
| u32 sensor_id) |
| { |
| void __iomem *srot_addr; |
| unsigned int srot_val, sensor_en; |
| |
| srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4); |
| srot_val = readl_relaxed(srot_addr); |
| srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val); |
| |
| sensor_en = ((1 << sensor_id) & srot_val); |
| |
| return sensor_en; |
| } |
| |
| static int tsens2xxx_hw_init(struct tsens_device *tmdev) |
| { |
| int rc = 0; |
| |
| rc = __tsens2xxx_hw_init(tmdev); |
| if (rc) |
| return rc; |
| |
| spin_lock_init(&tmdev->tsens_crit_lock); |
| spin_lock_init(&tmdev->tsens_upp_low_lock); |
| |
| if (tmdev->ctrl_data->mtc) { |
| if (tmdev->ops->dbg) |
| tmdev->ops->dbg(tmdev, 0, TSENS_DBG_MTC_DATA, NULL); |
| } |
| |
| return 0; |
| } |
| |
| static const struct tsens_irqs tsens2xxx_irqs[] = { |
| { "tsens-upper-lower", tsens_tm_irq_thread}, |
| { "tsens-critical", tsens_tm_critical_irq_thread}, |
| { "tsens-0C", tsens_tm_0C_irq_thread}, |
| }; |
| |
| static int tsens2xxx_register_interrupts(struct tsens_device *tmdev) |
| { |
| struct platform_device *pdev; |
| int i, rc, irq_no; |
| unsigned long irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; |
| |
| if (!tmdev) |
| return -EINVAL; |
| |
| if (tmdev->min_temp_sensor_id != MIN_TEMP_DEF_OFFSET) |
| irq_no = ARRAY_SIZE(tsens2xxx_irqs); |
| else |
| irq_no = ARRAY_SIZE(tsens2xxx_irqs) - 1; |
| |
| pdev = tmdev->pdev; |
| |
| for (i = 0; i < irq_no; i++) { |
| int irq; |
| |
| irq = platform_get_irq_byname(pdev, tsens2xxx_irqs[i].name); |
| if (irq < 0) { |
| dev_err(&pdev->dev, "failed to get irq %s\n", |
| tsens2xxx_irqs[i].name); |
| return irq; |
| } |
| |
| if (i == 2) |
| irqflags = IRQF_TRIGGER_RISING | IRQF_ONESHOT; |
| |
| rc = devm_request_threaded_irq(&pdev->dev, irq, NULL, |
| tsens2xxx_irqs[i].handler, |
| irqflags, tsens2xxx_irqs[i].name, tmdev); |
| if (rc) { |
| dev_err(&pdev->dev, "failed to get irq %s\n", |
| tsens2xxx_irqs[i].name); |
| return rc; |
| } |
| enable_irq_wake(irq); |
| } |
| |
| return 0; |
| } |
| |
| static const struct tsens_ops ops_tsens2xxx = { |
| .hw_init = tsens2xxx_hw_init, |
| .get_temp = tsens2xxx_get_temp, |
| .set_trips = tsens2xxx_set_trip_temp, |
| .interrupts_reg = tsens2xxx_register_interrupts, |
| .dbg = tsens2xxx_dbg, |
| .sensor_en = tsens2xxx_hw_sensor_en, |
| }; |
| |
| const struct tsens_data data_tsens2xxx = { |
| .cycle_monitor = false, |
| .cycle_compltn_monitor_mask = 1, |
| .wd_bark = false, |
| .wd_bark_mask = 1, |
| .ops = &ops_tsens2xxx, |
| .mtc = true, |
| }; |
| |
| const struct tsens_data data_tsens23xx = { |
| .cycle_monitor = true, |
| .cycle_compltn_monitor_mask = 1, |
| .wd_bark = true, |
| .wd_bark_mask = 1, |
| .ops = &ops_tsens2xxx, |
| .mtc = false, |
| .ver_major = 2, |
| .ver_minor = 3, |
| }; |
| |
| const struct tsens_data data_tsens24xx = { |
| .cycle_monitor = true, |
| .cycle_compltn_monitor_mask = 1, |
| .wd_bark = true, |
| /* Enable Watchdog monitoring by unmasking */ |
| .wd_bark_mask = 0, |
| .ops = &ops_tsens2xxx, |
| .mtc = false, |
| }; |
| |
| const struct tsens_data data_tsens26xx = { |
| .cycle_monitor = true, |
| .cycle_compltn_monitor_mask = 1, |
| .wd_bark = true, |
| .wd_bark_mask = 0, |
| .ops = &ops_tsens2xxx, |
| .mtc = false, |
| .ver_major = 2, |
| .ver_minor = 6, |
| }; |