| /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/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" |
| |
| #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_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_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_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 |
| |
| 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_get_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; |
| |
| 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)) { |
| pr_err("TSENS device first round not complete0x%x\n", code); |
| return -ENODATA; |
| } |
| |
| 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; |
| } |
| |
| static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor, |
| int trip, enum thermal_trip_activation_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_TRIP_ACTIVATION_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_TRIP_ACTIVATION_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_TRIP_ACTIVATION_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; |
| |
| 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_TRIP_ACTIVATION_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_TRIP_ACTIVATION_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_TRIP_ACTIVATION_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_TRIP_ACTIVATION_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); |
| 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_TRIP_ACTIVATION_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_TRIP_ACTIVATION_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 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) |
| { |
| 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)); |
| |
| 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}, |
| }; |
| |
| static int tsens2xxx_register_interrupts(struct tsens_device *tmdev) |
| { |
| struct platform_device *pdev; |
| int i, rc; |
| |
| if (!tmdev) |
| return -EINVAL; |
| |
| pdev = tmdev->pdev; |
| |
| for (i = 0; i < ARRAY_SIZE(tsens2xxx_irqs); 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; |
| } |
| |
| rc = devm_request_threaded_irq(&pdev->dev, irq, NULL, |
| tsens2xxx_irqs[i].handler, |
| IRQF_TRIGGER_HIGH | IRQF_ONESHOT, |
| 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, |
| }; |
| |
| 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, |
| }; |