12d71d8deSAmit Kucheria // SPDX-License-Identifier: GPL-2.0 29066073cSRajendra Nayak /* 39066073cSRajendra Nayak * Copyright (c) 2015, The Linux Foundation. All rights reserved. 4a7ff8297SAmit Kucheria * Copyright (c) 2019, 2020, Linaro Ltd. 59066073cSRajendra Nayak */ 69066073cSRajendra Nayak 77c938f48SAmit Kucheria #include <linux/debugfs.h> 89066073cSRajendra Nayak #include <linux/err.h> 9a7ff8297SAmit Kucheria #include <linux/io.h> 109066073cSRajendra Nayak #include <linux/module.h> 11a7ff8297SAmit Kucheria #include <linux/nvmem-consumer.h> 129066073cSRajendra Nayak #include <linux/of.h> 13a7ff8297SAmit Kucheria #include <linux/of_address.h> 14634e11d5SAmit Kucheria #include <linux/of_platform.h> 1553e2a20eSAnsuel Smith #include <linux/mfd/syscon.h> 169066073cSRajendra Nayak #include <linux/platform_device.h> 179066073cSRajendra Nayak #include <linux/pm.h> 18a7ff8297SAmit Kucheria #include <linux/regmap.h> 199066073cSRajendra Nayak #include <linux/slab.h> 20*34b9a92bSPriyansh Jain #include <linux/suspend.h> 219066073cSRajendra Nayak #include <linux/thermal.h> 228556e19dSDmitry Baryshkov #include "../thermal_hwmon.h" 239066073cSRajendra Nayak #include "tsens.h" 249066073cSRajendra Nayak 25a7ff8297SAmit Kucheria /** 26a7ff8297SAmit Kucheria * struct tsens_irq_data - IRQ status and temperature violations 27a7ff8297SAmit Kucheria * @up_viol: upper threshold violated 28a7ff8297SAmit Kucheria * @up_thresh: upper threshold temperature value 29a7ff8297SAmit Kucheria * @up_irq_mask: mask register for upper threshold irqs 30a7ff8297SAmit Kucheria * @up_irq_clear: clear register for uppper threshold irqs 31a7ff8297SAmit Kucheria * @low_viol: lower threshold violated 32a7ff8297SAmit Kucheria * @low_thresh: lower threshold temperature value 33a7ff8297SAmit Kucheria * @low_irq_mask: mask register for lower threshold irqs 34a7ff8297SAmit Kucheria * @low_irq_clear: clear register for lower threshold irqs 35a7ff8297SAmit Kucheria * @crit_viol: critical threshold violated 36a7ff8297SAmit Kucheria * @crit_thresh: critical threshold temperature value 37a7ff8297SAmit Kucheria * @crit_irq_mask: mask register for critical threshold irqs 38a7ff8297SAmit Kucheria * @crit_irq_clear: clear register for critical threshold irqs 39a7ff8297SAmit Kucheria * 40a7ff8297SAmit Kucheria * Structure containing data about temperature threshold settings and 41a7ff8297SAmit Kucheria * irq status if they were violated. 42a7ff8297SAmit Kucheria */ 43a7ff8297SAmit Kucheria struct tsens_irq_data { 44a7ff8297SAmit Kucheria u32 up_viol; 45a7ff8297SAmit Kucheria int up_thresh; 46a7ff8297SAmit Kucheria u32 up_irq_mask; 47a7ff8297SAmit Kucheria u32 up_irq_clear; 48a7ff8297SAmit Kucheria u32 low_viol; 49a7ff8297SAmit Kucheria int low_thresh; 50a7ff8297SAmit Kucheria u32 low_irq_mask; 51a7ff8297SAmit Kucheria u32 low_irq_clear; 52a7ff8297SAmit Kucheria u32 crit_viol; 53a7ff8297SAmit Kucheria u32 crit_thresh; 54a7ff8297SAmit Kucheria u32 crit_irq_mask; 55a7ff8297SAmit Kucheria u32 crit_irq_clear; 56a7ff8297SAmit Kucheria }; 57a7ff8297SAmit Kucheria 58a7ff8297SAmit Kucheria char *qfprom_read(struct device *dev, const char *cname) 59a7ff8297SAmit Kucheria { 60a7ff8297SAmit Kucheria struct nvmem_cell *cell; 61a7ff8297SAmit Kucheria ssize_t data; 62a7ff8297SAmit Kucheria char *ret; 63a7ff8297SAmit Kucheria 64a7ff8297SAmit Kucheria cell = nvmem_cell_get(dev, cname); 65a7ff8297SAmit Kucheria if (IS_ERR(cell)) 66a7ff8297SAmit Kucheria return ERR_CAST(cell); 67a7ff8297SAmit Kucheria 68a7ff8297SAmit Kucheria ret = nvmem_cell_read(cell, &data); 69a7ff8297SAmit Kucheria nvmem_cell_put(cell); 70a7ff8297SAmit Kucheria 71a7ff8297SAmit Kucheria return ret; 72a7ff8297SAmit Kucheria } 73a7ff8297SAmit Kucheria 74439f2409SDmitry Baryshkov int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup) 75498d2457SDmitry Baryshkov { 76498d2457SDmitry Baryshkov u32 mode; 77498d2457SDmitry Baryshkov u32 base1, base2; 78439f2409SDmitry Baryshkov char name[] = "sXX_pY_backup"; /* s10_p1_backup */ 79498d2457SDmitry Baryshkov int i, ret; 80498d2457SDmitry Baryshkov 81498d2457SDmitry Baryshkov if (priv->num_sensors > MAX_SENSORS) 82498d2457SDmitry Baryshkov return -EINVAL; 83498d2457SDmitry Baryshkov 84439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "mode%s", backup ? "_backup" : ""); 85439f2409SDmitry Baryshkov if (ret < 0) 86439f2409SDmitry Baryshkov return ret; 87439f2409SDmitry Baryshkov 88439f2409SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &mode); 89498d2457SDmitry Baryshkov if (ret == -ENOENT) 90498d2457SDmitry Baryshkov dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n"); 91498d2457SDmitry Baryshkov if (ret < 0) 92498d2457SDmitry Baryshkov return ret; 93498d2457SDmitry Baryshkov 94498d2457SDmitry Baryshkov dev_dbg(priv->dev, "calibration mode is %d\n", mode); 95498d2457SDmitry Baryshkov 96439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "base1%s", backup ? "_backup" : ""); 97498d2457SDmitry Baryshkov if (ret < 0) 98498d2457SDmitry Baryshkov return ret; 99498d2457SDmitry Baryshkov 100439f2409SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base1); 101439f2409SDmitry Baryshkov if (ret < 0) 102439f2409SDmitry Baryshkov return ret; 103439f2409SDmitry Baryshkov 104439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "base2%s", backup ? "_backup" : ""); 105439f2409SDmitry Baryshkov if (ret < 0) 106439f2409SDmitry Baryshkov return ret; 107439f2409SDmitry Baryshkov 108439f2409SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base2); 109498d2457SDmitry Baryshkov if (ret < 0) 110498d2457SDmitry Baryshkov return ret; 111498d2457SDmitry Baryshkov 112498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) { 113439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "s%d_p1%s", priv->sensor[i].hw_id, 114439f2409SDmitry Baryshkov backup ? "_backup" : ""); 115498d2457SDmitry Baryshkov if (ret < 0) 116498d2457SDmitry Baryshkov return ret; 117498d2457SDmitry Baryshkov 118498d2457SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p1[i]); 119498d2457SDmitry Baryshkov if (ret) 120498d2457SDmitry Baryshkov return ret; 121498d2457SDmitry Baryshkov 122439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "s%d_p2%s", priv->sensor[i].hw_id, 123439f2409SDmitry Baryshkov backup ? "_backup" : ""); 124498d2457SDmitry Baryshkov if (ret < 0) 125498d2457SDmitry Baryshkov return ret; 126498d2457SDmitry Baryshkov 127498d2457SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p2[i]); 128498d2457SDmitry Baryshkov if (ret) 129498d2457SDmitry Baryshkov return ret; 130498d2457SDmitry Baryshkov } 131498d2457SDmitry Baryshkov 132498d2457SDmitry Baryshkov switch (mode) { 133498d2457SDmitry Baryshkov case ONE_PT_CALIB: 134498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) 135498d2457SDmitry Baryshkov p1[i] = p1[i] + (base1 << shift); 136498d2457SDmitry Baryshkov break; 137498d2457SDmitry Baryshkov case TWO_PT_CALIB: 138b6f739daSStephan Gerhold case TWO_PT_CALIB_NO_OFFSET: 139498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) 140498d2457SDmitry Baryshkov p2[i] = (p2[i] + base2) << shift; 141498d2457SDmitry Baryshkov fallthrough; 142498d2457SDmitry Baryshkov case ONE_PT_CALIB2: 143b6f739daSStephan Gerhold case ONE_PT_CALIB2_NO_OFFSET: 144498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) 145498d2457SDmitry Baryshkov p1[i] = (p1[i] + base1) << shift; 146498d2457SDmitry Baryshkov break; 147498d2457SDmitry Baryshkov default: 148498d2457SDmitry Baryshkov dev_dbg(priv->dev, "calibrationless mode\n"); 149498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) { 150498d2457SDmitry Baryshkov p1[i] = 500; 151498d2457SDmitry Baryshkov p2[i] = 780; 152498d2457SDmitry Baryshkov } 153498d2457SDmitry Baryshkov } 154498d2457SDmitry Baryshkov 155b6f739daSStephan Gerhold /* Apply calibration offset workaround except for _NO_OFFSET modes */ 156b6f739daSStephan Gerhold switch (mode) { 157b6f739daSStephan Gerhold case TWO_PT_CALIB: 158b6f739daSStephan Gerhold for (i = 0; i < priv->num_sensors; i++) 159b6f739daSStephan Gerhold p2[i] += priv->sensor[i].p2_calib_offset; 160b6f739daSStephan Gerhold fallthrough; 161b6f739daSStephan Gerhold case ONE_PT_CALIB2: 162b6f739daSStephan Gerhold for (i = 0; i < priv->num_sensors; i++) 163b6f739daSStephan Gerhold p1[i] += priv->sensor[i].p1_calib_offset; 164b6f739daSStephan Gerhold break; 165b6f739daSStephan Gerhold } 166b6f739daSStephan Gerhold 167439f2409SDmitry Baryshkov return mode; 168439f2409SDmitry Baryshkov } 169439f2409SDmitry Baryshkov 170439f2409SDmitry Baryshkov int tsens_calibrate_nvmem(struct tsens_priv *priv, int shift) 171439f2409SDmitry Baryshkov { 172439f2409SDmitry Baryshkov u32 p1[MAX_SENSORS], p2[MAX_SENSORS]; 173439f2409SDmitry Baryshkov int mode; 174439f2409SDmitry Baryshkov 175439f2409SDmitry Baryshkov mode = tsens_read_calibration(priv, shift, p1, p2, false); 176439f2409SDmitry Baryshkov if (mode < 0) 177439f2409SDmitry Baryshkov return mode; 178439f2409SDmitry Baryshkov 179498d2457SDmitry Baryshkov compute_intercept_slope(priv, p1, p2, mode); 180498d2457SDmitry Baryshkov 181498d2457SDmitry Baryshkov return 0; 182498d2457SDmitry Baryshkov } 183498d2457SDmitry Baryshkov 184498d2457SDmitry Baryshkov int tsens_calibrate_common(struct tsens_priv *priv) 185498d2457SDmitry Baryshkov { 186498d2457SDmitry Baryshkov return tsens_calibrate_nvmem(priv, 2); 187498d2457SDmitry Baryshkov } 188498d2457SDmitry Baryshkov 189913d32e2SDmitry Baryshkov static u32 tsens_read_cell(const struct tsens_single_value *cell, u8 len, u32 *data0, u32 *data1) 190913d32e2SDmitry Baryshkov { 191913d32e2SDmitry Baryshkov u32 val; 192913d32e2SDmitry Baryshkov u32 *data = cell->blob ? data1 : data0; 193913d32e2SDmitry Baryshkov 194913d32e2SDmitry Baryshkov if (cell->shift + len <= 32) { 195913d32e2SDmitry Baryshkov val = data[cell->idx] >> cell->shift; 196913d32e2SDmitry Baryshkov } else { 197913d32e2SDmitry Baryshkov u8 part = 32 - cell->shift; 198913d32e2SDmitry Baryshkov 199913d32e2SDmitry Baryshkov val = data[cell->idx] >> cell->shift; 200913d32e2SDmitry Baryshkov val |= data[cell->idx + 1] << part; 201913d32e2SDmitry Baryshkov } 202913d32e2SDmitry Baryshkov 203913d32e2SDmitry Baryshkov return val & ((1 << len) - 1); 204913d32e2SDmitry Baryshkov } 205913d32e2SDmitry Baryshkov 206913d32e2SDmitry Baryshkov int tsens_read_calibration_legacy(struct tsens_priv *priv, 207913d32e2SDmitry Baryshkov const struct tsens_legacy_calibration_format *format, 208913d32e2SDmitry Baryshkov u32 *p1, u32 *p2, 209913d32e2SDmitry Baryshkov u32 *cdata0, u32 *cdata1) 210913d32e2SDmitry Baryshkov { 211913d32e2SDmitry Baryshkov u32 mode, invalid; 212913d32e2SDmitry Baryshkov u32 base1, base2; 213913d32e2SDmitry Baryshkov int i; 214913d32e2SDmitry Baryshkov 215913d32e2SDmitry Baryshkov mode = tsens_read_cell(&format->mode, 2, cdata0, cdata1); 216913d32e2SDmitry Baryshkov invalid = tsens_read_cell(&format->invalid, 1, cdata0, cdata1); 217913d32e2SDmitry Baryshkov if (invalid) 218913d32e2SDmitry Baryshkov mode = NO_PT_CALIB; 219913d32e2SDmitry Baryshkov dev_dbg(priv->dev, "calibration mode is %d\n", mode); 220913d32e2SDmitry Baryshkov 221913d32e2SDmitry Baryshkov base1 = tsens_read_cell(&format->base[0], format->base_len, cdata0, cdata1); 222913d32e2SDmitry Baryshkov base2 = tsens_read_cell(&format->base[1], format->base_len, cdata0, cdata1); 223913d32e2SDmitry Baryshkov 224913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) { 225913d32e2SDmitry Baryshkov p1[i] = tsens_read_cell(&format->sp[i][0], format->sp_len, cdata0, cdata1); 226913d32e2SDmitry Baryshkov p2[i] = tsens_read_cell(&format->sp[i][1], format->sp_len, cdata0, cdata1); 227913d32e2SDmitry Baryshkov } 228913d32e2SDmitry Baryshkov 229913d32e2SDmitry Baryshkov switch (mode) { 230913d32e2SDmitry Baryshkov case ONE_PT_CALIB: 231913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) 232913d32e2SDmitry Baryshkov p1[i] = p1[i] + (base1 << format->base_shift); 233913d32e2SDmitry Baryshkov break; 234913d32e2SDmitry Baryshkov case TWO_PT_CALIB: 235913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) 236913d32e2SDmitry Baryshkov p2[i] = (p2[i] + base2) << format->base_shift; 237913d32e2SDmitry Baryshkov fallthrough; 238913d32e2SDmitry Baryshkov case ONE_PT_CALIB2: 239913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) 240913d32e2SDmitry Baryshkov p1[i] = (p1[i] + base1) << format->base_shift; 241913d32e2SDmitry Baryshkov break; 242913d32e2SDmitry Baryshkov default: 243913d32e2SDmitry Baryshkov dev_dbg(priv->dev, "calibrationless mode\n"); 244913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) { 245913d32e2SDmitry Baryshkov p1[i] = 500; 246913d32e2SDmitry Baryshkov p2[i] = 780; 247913d32e2SDmitry Baryshkov } 248913d32e2SDmitry Baryshkov } 249913d32e2SDmitry Baryshkov 250913d32e2SDmitry Baryshkov return mode; 251913d32e2SDmitry Baryshkov } 252913d32e2SDmitry Baryshkov 253a7ff8297SAmit Kucheria /* 254a7ff8297SAmit Kucheria * Use this function on devices where slope and offset calculations 255a7ff8297SAmit Kucheria * depend on calibration data read from qfprom. On others the slope 256a7ff8297SAmit Kucheria * and offset values are derived from tz->tzp->slope and tz->tzp->offset 257a7ff8297SAmit Kucheria * resp. 258a7ff8297SAmit Kucheria */ 259a7ff8297SAmit Kucheria void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, 260a7ff8297SAmit Kucheria u32 *p2, u32 mode) 261a7ff8297SAmit Kucheria { 262a7ff8297SAmit Kucheria int i; 263a7ff8297SAmit Kucheria int num, den; 264a7ff8297SAmit Kucheria 265a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 266a7ff8297SAmit Kucheria dev_dbg(priv->dev, 267a7ff8297SAmit Kucheria "%s: sensor%d - data_point1:%#x data_point2:%#x\n", 268a7ff8297SAmit Kucheria __func__, i, p1[i], p2[i]); 269a7ff8297SAmit Kucheria 2709d51769bSAnsuel Smith if (!priv->sensor[i].slope) 271a7ff8297SAmit Kucheria priv->sensor[i].slope = SLOPE_DEFAULT; 272b6f739daSStephan Gerhold if (mode == TWO_PT_CALIB || mode == TWO_PT_CALIB_NO_OFFSET) { 273a7ff8297SAmit Kucheria /* 274a7ff8297SAmit Kucheria * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ 275a7ff8297SAmit Kucheria * temp_120_degc - temp_30_degc (x2 - x1) 276a7ff8297SAmit Kucheria */ 277a7ff8297SAmit Kucheria num = p2[i] - p1[i]; 278a7ff8297SAmit Kucheria num *= SLOPE_FACTOR; 279a7ff8297SAmit Kucheria den = CAL_DEGC_PT2 - CAL_DEGC_PT1; 280a7ff8297SAmit Kucheria priv->sensor[i].slope = num / den; 281a7ff8297SAmit Kucheria } 282a7ff8297SAmit Kucheria 283a7ff8297SAmit Kucheria priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - 284a7ff8297SAmit Kucheria (CAL_DEGC_PT1 * 285a7ff8297SAmit Kucheria priv->sensor[i].slope); 286a7ff8297SAmit Kucheria dev_dbg(priv->dev, "%s: offset:%d\n", __func__, 287a7ff8297SAmit Kucheria priv->sensor[i].offset); 288a7ff8297SAmit Kucheria } 289a7ff8297SAmit Kucheria } 290a7ff8297SAmit Kucheria 291a7ff8297SAmit Kucheria static inline u32 degc_to_code(int degc, const struct tsens_sensor *s) 292a7ff8297SAmit Kucheria { 293a7ff8297SAmit Kucheria u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR); 294a7ff8297SAmit Kucheria 295a7ff8297SAmit Kucheria pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc); 296a7ff8297SAmit Kucheria return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE); 297a7ff8297SAmit Kucheria } 298a7ff8297SAmit Kucheria 299a7ff8297SAmit Kucheria static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) 300a7ff8297SAmit Kucheria { 301a7ff8297SAmit Kucheria int degc, num, den; 302a7ff8297SAmit Kucheria 303a7ff8297SAmit Kucheria num = (adc_code * SLOPE_FACTOR) - s->offset; 304a7ff8297SAmit Kucheria den = s->slope; 305a7ff8297SAmit Kucheria 306a7ff8297SAmit Kucheria if (num > 0) 307a7ff8297SAmit Kucheria degc = num + (den / 2); 308a7ff8297SAmit Kucheria else if (num < 0) 309a7ff8297SAmit Kucheria degc = num - (den / 2); 310a7ff8297SAmit Kucheria else 311a7ff8297SAmit Kucheria degc = num; 312a7ff8297SAmit Kucheria 313a7ff8297SAmit Kucheria degc /= den; 314a7ff8297SAmit Kucheria 315a7ff8297SAmit Kucheria return degc; 316a7ff8297SAmit Kucheria } 317a7ff8297SAmit Kucheria 318a7ff8297SAmit Kucheria /** 319a7ff8297SAmit Kucheria * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. 320a7ff8297SAmit Kucheria * @s: Pointer to sensor struct 321a7ff8297SAmit Kucheria * @field: Index into regmap_field array pointing to temperature data 322a7ff8297SAmit Kucheria * 323a7ff8297SAmit Kucheria * This function handles temperature returned in ADC code or deciCelsius 324a7ff8297SAmit Kucheria * depending on IP version. 325a7ff8297SAmit Kucheria * 326a7ff8297SAmit Kucheria * Return: Temperature in milliCelsius on success, a negative errno will 327a7ff8297SAmit Kucheria * be returned in error cases 328a7ff8297SAmit Kucheria */ 329a7ff8297SAmit Kucheria static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) 330a7ff8297SAmit Kucheria { 331a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 332a7ff8297SAmit Kucheria u32 resolution; 333a7ff8297SAmit Kucheria u32 temp = 0; 334a7ff8297SAmit Kucheria int ret; 335a7ff8297SAmit Kucheria 336a7ff8297SAmit Kucheria resolution = priv->fields[LAST_TEMP_0].msb - 337a7ff8297SAmit Kucheria priv->fields[LAST_TEMP_0].lsb; 338a7ff8297SAmit Kucheria 339a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[field], &temp); 340a7ff8297SAmit Kucheria if (ret) 341a7ff8297SAmit Kucheria return ret; 342a7ff8297SAmit Kucheria 343a7ff8297SAmit Kucheria /* Convert temperature from ADC code to milliCelsius */ 344a7ff8297SAmit Kucheria if (priv->feat->adc) 345a7ff8297SAmit Kucheria return code_to_degc(temp, s) * 1000; 346a7ff8297SAmit Kucheria 347a7ff8297SAmit Kucheria /* deciCelsius -> milliCelsius along with sign extension */ 348a7ff8297SAmit Kucheria return sign_extend32(temp, resolution) * 100; 349a7ff8297SAmit Kucheria } 350a7ff8297SAmit Kucheria 351a7ff8297SAmit Kucheria /** 352a7ff8297SAmit Kucheria * tsens_mC_to_hw - Convert temperature to hardware register value 353a7ff8297SAmit Kucheria * @s: Pointer to sensor struct 354a7ff8297SAmit Kucheria * @temp: temperature in milliCelsius to be programmed to hardware 355a7ff8297SAmit Kucheria * 356a7ff8297SAmit Kucheria * This function outputs the value to be written to hardware in ADC code 357a7ff8297SAmit Kucheria * or deciCelsius depending on IP version. 358a7ff8297SAmit Kucheria * 359a7ff8297SAmit Kucheria * Return: ADC code or temperature in deciCelsius. 360a7ff8297SAmit Kucheria */ 361a7ff8297SAmit Kucheria static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp) 362a7ff8297SAmit Kucheria { 363a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 364a7ff8297SAmit Kucheria 365a7ff8297SAmit Kucheria /* milliC to adc code */ 366a7ff8297SAmit Kucheria if (priv->feat->adc) 367a7ff8297SAmit Kucheria return degc_to_code(temp / 1000, s); 368a7ff8297SAmit Kucheria 369a7ff8297SAmit Kucheria /* milliC to deciC */ 370a7ff8297SAmit Kucheria return temp / 100; 371a7ff8297SAmit Kucheria } 372a7ff8297SAmit Kucheria 373a7ff8297SAmit Kucheria static inline enum tsens_ver tsens_version(struct tsens_priv *priv) 374a7ff8297SAmit Kucheria { 375a7ff8297SAmit Kucheria return priv->feat->ver_major; 376a7ff8297SAmit Kucheria } 377a7ff8297SAmit Kucheria 378a7ff8297SAmit Kucheria static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, 379a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable) 380a7ff8297SAmit Kucheria { 381a7ff8297SAmit Kucheria u32 index = 0; 382a7ff8297SAmit Kucheria 383a7ff8297SAmit Kucheria switch (irq_type) { 384a7ff8297SAmit Kucheria case UPPER: 385a7ff8297SAmit Kucheria index = UP_INT_CLEAR_0 + hw_id; 386a7ff8297SAmit Kucheria break; 387a7ff8297SAmit Kucheria case LOWER: 388a7ff8297SAmit Kucheria index = LOW_INT_CLEAR_0 + hw_id; 389a7ff8297SAmit Kucheria break; 390a7ff8297SAmit Kucheria case CRITICAL: 391a7ff8297SAmit Kucheria /* No critical interrupts before v2 */ 392a7ff8297SAmit Kucheria return; 393a7ff8297SAmit Kucheria } 394a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index], enable ? 0 : 1); 395a7ff8297SAmit Kucheria } 396a7ff8297SAmit Kucheria 397a7ff8297SAmit Kucheria static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id, 398a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable) 399a7ff8297SAmit Kucheria { 400a7ff8297SAmit Kucheria u32 index_mask = 0, index_clear = 0; 401a7ff8297SAmit Kucheria 402a7ff8297SAmit Kucheria /* 403a7ff8297SAmit Kucheria * To enable the interrupt flag for a sensor: 404a7ff8297SAmit Kucheria * - clear the mask bit 405a7ff8297SAmit Kucheria * To disable the interrupt flag for a sensor: 406a7ff8297SAmit Kucheria * - Mask further interrupts for this sensor 407a7ff8297SAmit Kucheria * - Write 1 followed by 0 to clear the interrupt 408a7ff8297SAmit Kucheria */ 409a7ff8297SAmit Kucheria switch (irq_type) { 410a7ff8297SAmit Kucheria case UPPER: 411a7ff8297SAmit Kucheria index_mask = UP_INT_MASK_0 + hw_id; 412a7ff8297SAmit Kucheria index_clear = UP_INT_CLEAR_0 + hw_id; 413a7ff8297SAmit Kucheria break; 414a7ff8297SAmit Kucheria case LOWER: 415a7ff8297SAmit Kucheria index_mask = LOW_INT_MASK_0 + hw_id; 416a7ff8297SAmit Kucheria index_clear = LOW_INT_CLEAR_0 + hw_id; 417a7ff8297SAmit Kucheria break; 418a7ff8297SAmit Kucheria case CRITICAL: 419a7ff8297SAmit Kucheria index_mask = CRIT_INT_MASK_0 + hw_id; 420a7ff8297SAmit Kucheria index_clear = CRIT_INT_CLEAR_0 + hw_id; 421a7ff8297SAmit Kucheria break; 422a7ff8297SAmit Kucheria } 423a7ff8297SAmit Kucheria 424a7ff8297SAmit Kucheria if (enable) { 425a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_mask], 0); 426a7ff8297SAmit Kucheria } else { 427a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_mask], 1); 428a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_clear], 1); 429a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_clear], 0); 430a7ff8297SAmit Kucheria } 431a7ff8297SAmit Kucheria } 432a7ff8297SAmit Kucheria 433a7ff8297SAmit Kucheria /** 434a7ff8297SAmit Kucheria * tsens_set_interrupt - Set state of an interrupt 435a7ff8297SAmit Kucheria * @priv: Pointer to tsens controller private data 436a7ff8297SAmit Kucheria * @hw_id: Hardware ID aka. sensor number 437a7ff8297SAmit Kucheria * @irq_type: irq_type from enum tsens_irq_type 438a7ff8297SAmit Kucheria * @enable: false = disable, true = enable 439a7ff8297SAmit Kucheria * 440a7ff8297SAmit Kucheria * Call IP-specific function to set state of an interrupt 441a7ff8297SAmit Kucheria * 442a7ff8297SAmit Kucheria * Return: void 443a7ff8297SAmit Kucheria */ 444a7ff8297SAmit Kucheria static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id, 445a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable) 446a7ff8297SAmit Kucheria { 447a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__, 448a7ff8297SAmit Kucheria irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW", 449a7ff8297SAmit Kucheria enable ? "en" : "dis"); 450a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X) 451a7ff8297SAmit Kucheria tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); 452a7ff8297SAmit Kucheria else 453a7ff8297SAmit Kucheria tsens_set_interrupt_v1(priv, hw_id, irq_type, enable); 454a7ff8297SAmit Kucheria } 455a7ff8297SAmit Kucheria 456a7ff8297SAmit Kucheria /** 457a7ff8297SAmit Kucheria * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold 458a7ff8297SAmit Kucheria * @priv: Pointer to tsens controller private data 459a7ff8297SAmit Kucheria * @hw_id: Hardware ID aka. sensor number 460a7ff8297SAmit Kucheria * @d: Pointer to irq state data 461a7ff8297SAmit Kucheria * 462a7ff8297SAmit Kucheria * Return: 0 if threshold was not violated, 1 if it was violated and negative 463a7ff8297SAmit Kucheria * errno in case of errors 464a7ff8297SAmit Kucheria */ 465a7ff8297SAmit Kucheria static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, 466a7ff8297SAmit Kucheria struct tsens_irq_data *d) 467a7ff8297SAmit Kucheria { 468a7ff8297SAmit Kucheria int ret; 469a7ff8297SAmit Kucheria 470a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol); 471a7ff8297SAmit Kucheria if (ret) 472a7ff8297SAmit Kucheria return ret; 473a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol); 474a7ff8297SAmit Kucheria if (ret) 475a7ff8297SAmit Kucheria return ret; 476a7ff8297SAmit Kucheria 477a7ff8297SAmit Kucheria if (priv->feat->crit_int) { 478a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id], 479a7ff8297SAmit Kucheria &d->crit_viol); 480a7ff8297SAmit Kucheria if (ret) 481a7ff8297SAmit Kucheria return ret; 482a7ff8297SAmit Kucheria } 483a7ff8297SAmit Kucheria 484a7ff8297SAmit Kucheria if (d->up_viol || d->low_viol || d->crit_viol) 485a7ff8297SAmit Kucheria return 1; 486a7ff8297SAmit Kucheria 487a7ff8297SAmit Kucheria return 0; 488a7ff8297SAmit Kucheria } 489a7ff8297SAmit Kucheria 490a7ff8297SAmit Kucheria static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, 491a7ff8297SAmit Kucheria const struct tsens_sensor *s, 492a7ff8297SAmit Kucheria struct tsens_irq_data *d) 493a7ff8297SAmit Kucheria { 494a7ff8297SAmit Kucheria int ret; 495a7ff8297SAmit Kucheria 496a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear); 497a7ff8297SAmit Kucheria if (ret) 498a7ff8297SAmit Kucheria return ret; 499a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); 500a7ff8297SAmit Kucheria if (ret) 501a7ff8297SAmit Kucheria return ret; 502a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X) { 503a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); 504a7ff8297SAmit Kucheria if (ret) 505a7ff8297SAmit Kucheria return ret; 506a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); 507a7ff8297SAmit Kucheria if (ret) 508a7ff8297SAmit Kucheria return ret; 509a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id], 510a7ff8297SAmit Kucheria &d->crit_irq_clear); 511a7ff8297SAmit Kucheria if (ret) 512a7ff8297SAmit Kucheria return ret; 513a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id], 514a7ff8297SAmit Kucheria &d->crit_irq_mask); 515a7ff8297SAmit Kucheria if (ret) 516a7ff8297SAmit Kucheria return ret; 517a7ff8297SAmit Kucheria 518a7ff8297SAmit Kucheria d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); 519a7ff8297SAmit Kucheria } else { 520a7ff8297SAmit Kucheria /* No mask register on older TSENS */ 521a7ff8297SAmit Kucheria d->up_irq_mask = 0; 522a7ff8297SAmit Kucheria d->low_irq_mask = 0; 523a7ff8297SAmit Kucheria d->crit_irq_clear = 0; 524a7ff8297SAmit Kucheria d->crit_irq_mask = 0; 525a7ff8297SAmit Kucheria d->crit_thresh = 0; 526a7ff8297SAmit Kucheria } 527a7ff8297SAmit Kucheria 528a7ff8297SAmit Kucheria d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); 529a7ff8297SAmit Kucheria d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); 530a7ff8297SAmit Kucheria 531a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", 532a7ff8297SAmit Kucheria hw_id, __func__, 533a7ff8297SAmit Kucheria (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", 534a7ff8297SAmit Kucheria d->low_viol, d->up_viol, d->crit_viol, 535a7ff8297SAmit Kucheria d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear, 536a7ff8297SAmit Kucheria d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask); 537a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__, 538a7ff8297SAmit Kucheria (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", 539a7ff8297SAmit Kucheria d->low_thresh, d->up_thresh, d->crit_thresh); 540a7ff8297SAmit Kucheria 541a7ff8297SAmit Kucheria return 0; 542a7ff8297SAmit Kucheria } 543a7ff8297SAmit Kucheria 544a7ff8297SAmit Kucheria static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) 545a7ff8297SAmit Kucheria { 546a7ff8297SAmit Kucheria if (ver > VER_1_X) 547a7ff8297SAmit Kucheria return mask & (1 << hw_id); 548a7ff8297SAmit Kucheria 549a7ff8297SAmit Kucheria /* v1, v0.1 don't have a irq mask register */ 550a7ff8297SAmit Kucheria return 0; 551a7ff8297SAmit Kucheria } 552a7ff8297SAmit Kucheria 553a7ff8297SAmit Kucheria /** 554a7ff8297SAmit Kucheria * tsens_critical_irq_thread() - Threaded handler for critical interrupts 555a7ff8297SAmit Kucheria * @irq: irq number 556a7ff8297SAmit Kucheria * @data: tsens controller private data 557a7ff8297SAmit Kucheria * 558a7ff8297SAmit Kucheria * Check FSM watchdog bark status and clear if needed. 559a7ff8297SAmit Kucheria * Check all sensors to find ones that violated their critical threshold limits. 560a7ff8297SAmit Kucheria * Clear and then re-enable the interrupt. 561a7ff8297SAmit Kucheria * 562a7ff8297SAmit Kucheria * The level-triggered interrupt might deassert if the temperature returned to 563a7ff8297SAmit Kucheria * within the threshold limits by the time the handler got scheduled. We 564a7ff8297SAmit Kucheria * consider the irq to have been handled in that case. 565a7ff8297SAmit Kucheria * 566a7ff8297SAmit Kucheria * Return: IRQ_HANDLED 567a7ff8297SAmit Kucheria */ 5683ecc8292SAmit Kucheria static irqreturn_t tsens_critical_irq_thread(int irq, void *data) 569a7ff8297SAmit Kucheria { 570a7ff8297SAmit Kucheria struct tsens_priv *priv = data; 571a7ff8297SAmit Kucheria struct tsens_irq_data d; 572a7ff8297SAmit Kucheria int temp, ret, i; 573a7ff8297SAmit Kucheria u32 wdog_status, wdog_count; 574a7ff8297SAmit Kucheria 575a7ff8297SAmit Kucheria if (priv->feat->has_watchdog) { 576a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS], 577a7ff8297SAmit Kucheria &wdog_status); 578a7ff8297SAmit Kucheria if (ret) 579a7ff8297SAmit Kucheria return ret; 580a7ff8297SAmit Kucheria 581a7ff8297SAmit Kucheria if (wdog_status) { 582a7ff8297SAmit Kucheria /* Clear WDOG interrupt */ 583a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1); 584a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0); 585a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT], 586a7ff8297SAmit Kucheria &wdog_count); 587a7ff8297SAmit Kucheria if (ret) 588a7ff8297SAmit Kucheria return ret; 589a7ff8297SAmit Kucheria if (wdog_count) 590a7ff8297SAmit Kucheria dev_dbg(priv->dev, "%s: watchdog count: %d\n", 591a7ff8297SAmit Kucheria __func__, wdog_count); 592a7ff8297SAmit Kucheria 593a7ff8297SAmit Kucheria /* Fall through to handle critical interrupts if any */ 594a7ff8297SAmit Kucheria } 595a7ff8297SAmit Kucheria } 596a7ff8297SAmit Kucheria 597a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 598a7ff8297SAmit Kucheria const struct tsens_sensor *s = &priv->sensor[i]; 599a7ff8297SAmit Kucheria u32 hw_id = s->hw_id; 600a7ff8297SAmit Kucheria 601cf969218SAnsuel Smith if (!s->tzd) 602a7ff8297SAmit Kucheria continue; 603a7ff8297SAmit Kucheria if (!tsens_threshold_violated(priv, hw_id, &d)) 604a7ff8297SAmit Kucheria continue; 605a7ff8297SAmit Kucheria ret = get_temp_tsens_valid(s, &temp); 606a7ff8297SAmit Kucheria if (ret) { 607a7ff8297SAmit Kucheria dev_err(priv->dev, "[%u] %s: error reading sensor\n", 608a7ff8297SAmit Kucheria hw_id, __func__); 609a7ff8297SAmit Kucheria continue; 610a7ff8297SAmit Kucheria } 611a7ff8297SAmit Kucheria 612a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d); 613a7ff8297SAmit Kucheria if (d.crit_viol && 614a7ff8297SAmit Kucheria !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) { 615a7ff8297SAmit Kucheria /* Mask critical interrupts, unused on Linux */ 616a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, CRITICAL, false); 617a7ff8297SAmit Kucheria } 618a7ff8297SAmit Kucheria } 619a7ff8297SAmit Kucheria 620a7ff8297SAmit Kucheria return IRQ_HANDLED; 621a7ff8297SAmit Kucheria } 622a7ff8297SAmit Kucheria 623a7ff8297SAmit Kucheria /** 624a7ff8297SAmit Kucheria * tsens_irq_thread - Threaded interrupt handler for uplow interrupts 625a7ff8297SAmit Kucheria * @irq: irq number 626a7ff8297SAmit Kucheria * @data: tsens controller private data 627a7ff8297SAmit Kucheria * 628a7ff8297SAmit Kucheria * Check all sensors to find ones that violated their threshold limits. If the 629a7ff8297SAmit Kucheria * temperature is still outside the limits, call thermal_zone_device_update() to 630a7ff8297SAmit Kucheria * update the thresholds, else re-enable the interrupts. 631a7ff8297SAmit Kucheria * 632a7ff8297SAmit Kucheria * The level-triggered interrupt might deassert if the temperature returned to 633a7ff8297SAmit Kucheria * within the threshold limits by the time the handler got scheduled. We 634a7ff8297SAmit Kucheria * consider the irq to have been handled in that case. 635a7ff8297SAmit Kucheria * 636a7ff8297SAmit Kucheria * Return: IRQ_HANDLED 637a7ff8297SAmit Kucheria */ 6383ecc8292SAmit Kucheria static irqreturn_t tsens_irq_thread(int irq, void *data) 639a7ff8297SAmit Kucheria { 640a7ff8297SAmit Kucheria struct tsens_priv *priv = data; 641a7ff8297SAmit Kucheria struct tsens_irq_data d; 642df715f26SDaniel Lezcano int i; 643a7ff8297SAmit Kucheria 644a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 645a7ff8297SAmit Kucheria const struct tsens_sensor *s = &priv->sensor[i]; 646a7ff8297SAmit Kucheria u32 hw_id = s->hw_id; 647a7ff8297SAmit Kucheria 648cf969218SAnsuel Smith if (!s->tzd) 649a7ff8297SAmit Kucheria continue; 650a7ff8297SAmit Kucheria if (!tsens_threshold_violated(priv, hw_id, &d)) 651a7ff8297SAmit Kucheria continue; 652a7ff8297SAmit Kucheria 653df715f26SDaniel Lezcano thermal_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED); 65453e2a20eSAnsuel Smith 65553e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) { 65653e2a20eSAnsuel Smith /* Constraint: There is only 1 interrupt control register for all 65753e2a20eSAnsuel Smith * 11 temperature sensor. So monitoring more than 1 sensor based 65853e2a20eSAnsuel Smith * on interrupts will yield inconsistent result. To overcome this 65953e2a20eSAnsuel Smith * issue we will monitor only sensor 0 which is the master sensor. 66053e2a20eSAnsuel Smith */ 66153e2a20eSAnsuel Smith break; 66253e2a20eSAnsuel Smith } 663a7ff8297SAmit Kucheria } 664a7ff8297SAmit Kucheria 665a7ff8297SAmit Kucheria return IRQ_HANDLED; 666a7ff8297SAmit Kucheria } 667a7ff8297SAmit Kucheria 6684360af35SRobert Marko /** 6694360af35SRobert Marko * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts 6704360af35SRobert Marko * @irq: irq number 6714360af35SRobert Marko * @data: tsens controller private data 6724360af35SRobert Marko * 6734360af35SRobert Marko * Handle the combined interrupt as if it were 2 separate interrupts, so call the 6744360af35SRobert Marko * critical handler first and then the up/low one. 6754360af35SRobert Marko * 6764360af35SRobert Marko * Return: IRQ_HANDLED 6774360af35SRobert Marko */ 6784360af35SRobert Marko static irqreturn_t tsens_combined_irq_thread(int irq, void *data) 6794360af35SRobert Marko { 6804360af35SRobert Marko irqreturn_t ret; 6814360af35SRobert Marko 6824360af35SRobert Marko ret = tsens_critical_irq_thread(irq, data); 6834360af35SRobert Marko if (ret != IRQ_HANDLED) 6844360af35SRobert Marko return ret; 6854360af35SRobert Marko 6864360af35SRobert Marko return tsens_irq_thread(irq, data); 6874360af35SRobert Marko } 6884360af35SRobert Marko 689ca1b9a9eSDaniel Lezcano static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high) 690a7ff8297SAmit Kucheria { 6915f68d078SDaniel Lezcano struct tsens_sensor *s = thermal_zone_device_priv(tz); 692a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 693a7ff8297SAmit Kucheria struct device *dev = priv->dev; 694a7ff8297SAmit Kucheria struct tsens_irq_data d; 695a7ff8297SAmit Kucheria unsigned long flags; 696a7ff8297SAmit Kucheria int high_val, low_val, cl_high, cl_low; 697a7ff8297SAmit Kucheria u32 hw_id = s->hw_id; 698a7ff8297SAmit Kucheria 69953e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) { 70053e2a20eSAnsuel Smith /* Pre v0.1 IP had a single register for each type of interrupt 70153e2a20eSAnsuel Smith * and thresholds 70253e2a20eSAnsuel Smith */ 70353e2a20eSAnsuel Smith hw_id = 0; 70453e2a20eSAnsuel Smith } 70553e2a20eSAnsuel Smith 706a7ff8297SAmit Kucheria dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", 707a7ff8297SAmit Kucheria hw_id, __func__, low, high); 708a7ff8297SAmit Kucheria 709f63bacedSRobert Marko cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp); 710f63bacedSRobert Marko cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp); 711a7ff8297SAmit Kucheria 712a7ff8297SAmit Kucheria high_val = tsens_mC_to_hw(s, cl_high); 713a7ff8297SAmit Kucheria low_val = tsens_mC_to_hw(s, cl_low); 714a7ff8297SAmit Kucheria 715a7ff8297SAmit Kucheria spin_lock_irqsave(&priv->ul_lock, flags); 716a7ff8297SAmit Kucheria 717a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d); 718a7ff8297SAmit Kucheria 719a7ff8297SAmit Kucheria /* Write the new thresholds and clear the status */ 720a7ff8297SAmit Kucheria regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val); 721a7ff8297SAmit Kucheria regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val); 722a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, LOWER, true); 723a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, UPPER, true); 724a7ff8297SAmit Kucheria 725a7ff8297SAmit Kucheria spin_unlock_irqrestore(&priv->ul_lock, flags); 726a7ff8297SAmit Kucheria 727a7ff8297SAmit Kucheria dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n", 728a7ff8297SAmit Kucheria hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high); 729a7ff8297SAmit Kucheria 730a7ff8297SAmit Kucheria return 0; 731a7ff8297SAmit Kucheria } 732a7ff8297SAmit Kucheria 7333ecc8292SAmit Kucheria static int tsens_enable_irq(struct tsens_priv *priv) 734a7ff8297SAmit Kucheria { 735a7ff8297SAmit Kucheria int ret; 736a7ff8297SAmit Kucheria int val = tsens_version(priv) > VER_1_X ? 7 : 1; 737a7ff8297SAmit Kucheria 738a7ff8297SAmit Kucheria ret = regmap_field_write(priv->rf[INT_EN], val); 739a7ff8297SAmit Kucheria if (ret < 0) 740a7ff8297SAmit Kucheria dev_err(priv->dev, "%s: failed to enable interrupts\n", 741a7ff8297SAmit Kucheria __func__); 742a7ff8297SAmit Kucheria 743a7ff8297SAmit Kucheria return ret; 744a7ff8297SAmit Kucheria } 745a7ff8297SAmit Kucheria 7463ecc8292SAmit Kucheria static void tsens_disable_irq(struct tsens_priv *priv) 747a7ff8297SAmit Kucheria { 748a7ff8297SAmit Kucheria regmap_field_write(priv->rf[INT_EN], 0); 749a7ff8297SAmit Kucheria } 750a7ff8297SAmit Kucheria 751a7ff8297SAmit Kucheria int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) 752a7ff8297SAmit Kucheria { 753a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 754a7ff8297SAmit Kucheria int hw_id = s->hw_id; 755a7ff8297SAmit Kucheria u32 temp_idx = LAST_TEMP_0 + hw_id; 756a7ff8297SAmit Kucheria u32 valid_idx = VALID_0 + hw_id; 757a7ff8297SAmit Kucheria u32 valid; 758a7ff8297SAmit Kucheria int ret; 759a7ff8297SAmit Kucheria 76053e2a20eSAnsuel Smith /* VER_0 doesn't have VALID bit */ 761d012f918SAnsuel Smith if (tsens_version(priv) == VER_0) 762d012f918SAnsuel Smith goto get_temp; 763d012f918SAnsuel Smith 764a7ff8297SAmit Kucheria /* Valid bit is 0 for 6 AHB clock cycles. 765a7ff8297SAmit Kucheria * At 19.2MHz, 1 AHB clock is ~60ns. 766a7ff8297SAmit Kucheria * We should enter this loop very, very rarely. 767d012f918SAnsuel Smith * Wait 1 us since it's the min of poll_timeout macro. 768d012f918SAnsuel Smith * Old value was 400 ns. 769a7ff8297SAmit Kucheria */ 770d012f918SAnsuel Smith ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid, 771d012f918SAnsuel Smith valid, 1, 20 * USEC_PER_MSEC); 772a7ff8297SAmit Kucheria if (ret) 773a7ff8297SAmit Kucheria return ret; 774a7ff8297SAmit Kucheria 775d012f918SAnsuel Smith get_temp: 776a7ff8297SAmit Kucheria /* Valid bit is set, OK to read the temperature */ 777a7ff8297SAmit Kucheria *temp = tsens_hw_to_mC(s, temp_idx); 778a7ff8297SAmit Kucheria 779a7ff8297SAmit Kucheria return 0; 780a7ff8297SAmit Kucheria } 781a7ff8297SAmit Kucheria 782a7ff8297SAmit Kucheria int get_temp_common(const struct tsens_sensor *s, int *temp) 783a7ff8297SAmit Kucheria { 784a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 785a7ff8297SAmit Kucheria int hw_id = s->hw_id; 78653e2a20eSAnsuel Smith int last_temp = 0, ret, trdy; 78753e2a20eSAnsuel Smith unsigned long timeout; 78853e2a20eSAnsuel Smith 78953e2a20eSAnsuel Smith timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); 79053e2a20eSAnsuel Smith do { 79153e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) { 79253e2a20eSAnsuel Smith ret = regmap_field_read(priv->rf[TRDY], &trdy); 79353e2a20eSAnsuel Smith if (ret) 79453e2a20eSAnsuel Smith return ret; 79553e2a20eSAnsuel Smith if (!trdy) 79653e2a20eSAnsuel Smith continue; 79753e2a20eSAnsuel Smith } 798a7ff8297SAmit Kucheria 799a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); 800a7ff8297SAmit Kucheria if (ret) 801a7ff8297SAmit Kucheria return ret; 802a7ff8297SAmit Kucheria 803a7ff8297SAmit Kucheria *temp = code_to_degc(last_temp, s) * 1000; 804a7ff8297SAmit Kucheria 805a7ff8297SAmit Kucheria return 0; 80653e2a20eSAnsuel Smith } while (time_before(jiffies, timeout)); 80753e2a20eSAnsuel Smith 80853e2a20eSAnsuel Smith return -ETIMEDOUT; 809a7ff8297SAmit Kucheria } 810a7ff8297SAmit Kucheria 811a7ff8297SAmit Kucheria #ifdef CONFIG_DEBUG_FS 812a7ff8297SAmit Kucheria static int dbg_sensors_show(struct seq_file *s, void *data) 813a7ff8297SAmit Kucheria { 814a7ff8297SAmit Kucheria struct platform_device *pdev = s->private; 815a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 816a7ff8297SAmit Kucheria int i; 817a7ff8297SAmit Kucheria 818a7ff8297SAmit Kucheria seq_printf(s, "max: %2d\nnum: %2d\n\n", 819a7ff8297SAmit Kucheria priv->feat->max_sensors, priv->num_sensors); 820a7ff8297SAmit Kucheria 821a7ff8297SAmit Kucheria seq_puts(s, " id slope offset\n--------------------------\n"); 822a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 823a7ff8297SAmit Kucheria seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id, 824a7ff8297SAmit Kucheria priv->sensor[i].slope, priv->sensor[i].offset); 825a7ff8297SAmit Kucheria } 826a7ff8297SAmit Kucheria 827a7ff8297SAmit Kucheria return 0; 828a7ff8297SAmit Kucheria } 829a7ff8297SAmit Kucheria 830a7ff8297SAmit Kucheria static int dbg_version_show(struct seq_file *s, void *data) 831a7ff8297SAmit Kucheria { 832a7ff8297SAmit Kucheria struct platform_device *pdev = s->private; 833a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 834a7ff8297SAmit Kucheria u32 maj_ver, min_ver, step_ver; 835a7ff8297SAmit Kucheria int ret; 836a7ff8297SAmit Kucheria 837a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_0_1) { 838a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver); 839a7ff8297SAmit Kucheria if (ret) 840a7ff8297SAmit Kucheria return ret; 841a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver); 842a7ff8297SAmit Kucheria if (ret) 843a7ff8297SAmit Kucheria return ret; 844a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_STEP], &step_ver); 845a7ff8297SAmit Kucheria if (ret) 846a7ff8297SAmit Kucheria return ret; 847a7ff8297SAmit Kucheria seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver); 848a7ff8297SAmit Kucheria } else { 849c7e077e9SChristian Marangi seq_printf(s, "0.%d.0\n", priv->feat->ver_major); 850a7ff8297SAmit Kucheria } 851a7ff8297SAmit Kucheria 852a7ff8297SAmit Kucheria return 0; 853a7ff8297SAmit Kucheria } 854a7ff8297SAmit Kucheria 855a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_version); 856a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_sensors); 857a7ff8297SAmit Kucheria 858a7ff8297SAmit Kucheria static void tsens_debug_init(struct platform_device *pdev) 859a7ff8297SAmit Kucheria { 860a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 861a7ff8297SAmit Kucheria 86289992d95SChristian Marangi priv->debug_root = debugfs_lookup("tsens", NULL); 86389992d95SChristian Marangi if (!priv->debug_root) 864a7ff8297SAmit Kucheria priv->debug_root = debugfs_create_dir("tsens", NULL); 865a7ff8297SAmit Kucheria 866a7ff8297SAmit Kucheria /* A directory for each instance of the TSENS IP */ 867a7ff8297SAmit Kucheria priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root); 86889992d95SChristian Marangi debugfs_create_file("version", 0444, priv->debug, pdev, &dbg_version_fops); 869a7ff8297SAmit Kucheria debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops); 870a7ff8297SAmit Kucheria } 871a7ff8297SAmit Kucheria #else 872a7ff8297SAmit Kucheria static inline void tsens_debug_init(struct platform_device *pdev) {} 873a7ff8297SAmit Kucheria #endif 874a7ff8297SAmit Kucheria 875a7ff8297SAmit Kucheria static const struct regmap_config tsens_config = { 876a7ff8297SAmit Kucheria .name = "tm", 877a7ff8297SAmit Kucheria .reg_bits = 32, 878a7ff8297SAmit Kucheria .val_bits = 32, 879a7ff8297SAmit Kucheria .reg_stride = 4, 880a7ff8297SAmit Kucheria }; 881a7ff8297SAmit Kucheria 882a7ff8297SAmit Kucheria static const struct regmap_config tsens_srot_config = { 883a7ff8297SAmit Kucheria .name = "srot", 884a7ff8297SAmit Kucheria .reg_bits = 32, 885a7ff8297SAmit Kucheria .val_bits = 32, 886a7ff8297SAmit Kucheria .reg_stride = 4, 887a7ff8297SAmit Kucheria }; 888a7ff8297SAmit Kucheria 889a7ff8297SAmit Kucheria int __init init_common(struct tsens_priv *priv) 890a7ff8297SAmit Kucheria { 891a7ff8297SAmit Kucheria void __iomem *tm_base, *srot_base; 892a7ff8297SAmit Kucheria struct device *dev = priv->dev; 893a7ff8297SAmit Kucheria u32 ver_minor; 894a7ff8297SAmit Kucheria struct resource *res; 895a7ff8297SAmit Kucheria u32 enabled; 896a7ff8297SAmit Kucheria int ret, i, j; 897a7ff8297SAmit Kucheria struct platform_device *op = of_find_device_by_node(priv->dev->of_node); 898a7ff8297SAmit Kucheria 899a7ff8297SAmit Kucheria if (!op) 900a7ff8297SAmit Kucheria return -EINVAL; 901a7ff8297SAmit Kucheria 902a7ff8297SAmit Kucheria if (op->num_resources > 1) { 903a7ff8297SAmit Kucheria /* DT with separate SROT and TM address space */ 904a7ff8297SAmit Kucheria priv->tm_offset = 0; 905a7ff8297SAmit Kucheria res = platform_get_resource(op, IORESOURCE_MEM, 1); 906a7ff8297SAmit Kucheria srot_base = devm_ioremap_resource(dev, res); 907a7ff8297SAmit Kucheria if (IS_ERR(srot_base)) { 908a7ff8297SAmit Kucheria ret = PTR_ERR(srot_base); 909a7ff8297SAmit Kucheria goto err_put_device; 910a7ff8297SAmit Kucheria } 911a7ff8297SAmit Kucheria 912a7ff8297SAmit Kucheria priv->srot_map = devm_regmap_init_mmio(dev, srot_base, 913a7ff8297SAmit Kucheria &tsens_srot_config); 914a7ff8297SAmit Kucheria if (IS_ERR(priv->srot_map)) { 915a7ff8297SAmit Kucheria ret = PTR_ERR(priv->srot_map); 916a7ff8297SAmit Kucheria goto err_put_device; 917a7ff8297SAmit Kucheria } 918a7ff8297SAmit Kucheria } else { 919a7ff8297SAmit Kucheria /* old DTs where SROT and TM were in a contiguous 2K block */ 920a7ff8297SAmit Kucheria priv->tm_offset = 0x1000; 921a7ff8297SAmit Kucheria } 922a7ff8297SAmit Kucheria 92353e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1) { 924a7ff8297SAmit Kucheria res = platform_get_resource(op, IORESOURCE_MEM, 0); 925a7ff8297SAmit Kucheria tm_base = devm_ioremap_resource(dev, res); 926a7ff8297SAmit Kucheria if (IS_ERR(tm_base)) { 927a7ff8297SAmit Kucheria ret = PTR_ERR(tm_base); 928a7ff8297SAmit Kucheria goto err_put_device; 929a7ff8297SAmit Kucheria } 930a7ff8297SAmit Kucheria 931a7ff8297SAmit Kucheria priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); 93253e2a20eSAnsuel Smith } else { /* VER_0 share the same gcc regs using a syscon */ 93353e2a20eSAnsuel Smith struct device *parent = priv->dev->parent; 93453e2a20eSAnsuel Smith 93553e2a20eSAnsuel Smith if (parent) 93653e2a20eSAnsuel Smith priv->tm_map = syscon_node_to_regmap(parent->of_node); 93753e2a20eSAnsuel Smith } 93853e2a20eSAnsuel Smith 93953e2a20eSAnsuel Smith if (IS_ERR_OR_NULL(priv->tm_map)) { 94053e2a20eSAnsuel Smith if (!priv->tm_map) 94153e2a20eSAnsuel Smith ret = -ENODEV; 94253e2a20eSAnsuel Smith else 943a7ff8297SAmit Kucheria ret = PTR_ERR(priv->tm_map); 944a7ff8297SAmit Kucheria goto err_put_device; 945a7ff8297SAmit Kucheria } 946a7ff8297SAmit Kucheria 94753e2a20eSAnsuel Smith /* VER_0 have only tm_map */ 94853e2a20eSAnsuel Smith if (!priv->srot_map) 94953e2a20eSAnsuel Smith priv->srot_map = priv->tm_map; 95053e2a20eSAnsuel Smith 951a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_0_1) { 952a7ff8297SAmit Kucheria for (i = VER_MAJOR; i <= VER_STEP; i++) { 953a7ff8297SAmit Kucheria priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, 954a7ff8297SAmit Kucheria priv->fields[i]); 955f4136863SGuangqing Zhu if (IS_ERR(priv->rf[i])) { 956f4136863SGuangqing Zhu ret = PTR_ERR(priv->rf[i]); 957f4136863SGuangqing Zhu goto err_put_device; 958f4136863SGuangqing Zhu } 959a7ff8297SAmit Kucheria } 960a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); 961a7ff8297SAmit Kucheria if (ret) 962a7ff8297SAmit Kucheria goto err_put_device; 963a7ff8297SAmit Kucheria } 964a7ff8297SAmit Kucheria 965a7ff8297SAmit Kucheria priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, 966a7ff8297SAmit Kucheria priv->fields[TSENS_EN]); 967a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[TSENS_EN])) { 968a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[TSENS_EN]); 969a7ff8297SAmit Kucheria goto err_put_device; 970a7ff8297SAmit Kucheria } 97153e2a20eSAnsuel Smith /* in VER_0 TSENS need to be explicitly enabled */ 97253e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) 97353e2a20eSAnsuel Smith regmap_field_write(priv->rf[TSENS_EN], 1); 97453e2a20eSAnsuel Smith 975a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); 976a7ff8297SAmit Kucheria if (ret) 977a7ff8297SAmit Kucheria goto err_put_device; 978a7ff8297SAmit Kucheria if (!enabled) { 979a7ff8297SAmit Kucheria dev_err(dev, "%s: device not enabled\n", __func__); 980a7ff8297SAmit Kucheria ret = -ENODEV; 981a7ff8297SAmit Kucheria goto err_put_device; 982a7ff8297SAmit Kucheria } 983a7ff8297SAmit Kucheria 984a7ff8297SAmit Kucheria priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, 985a7ff8297SAmit Kucheria priv->fields[SENSOR_EN]); 986a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[SENSOR_EN])) { 987a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[SENSOR_EN]); 988a7ff8297SAmit Kucheria goto err_put_device; 989a7ff8297SAmit Kucheria } 990a7ff8297SAmit Kucheria priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map, 991a7ff8297SAmit Kucheria priv->fields[INT_EN]); 992a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[INT_EN])) { 993a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[INT_EN]); 994a7ff8297SAmit Kucheria goto err_put_device; 995a7ff8297SAmit Kucheria } 996a7ff8297SAmit Kucheria 99753e2a20eSAnsuel Smith priv->rf[TSENS_SW_RST] = 99853e2a20eSAnsuel Smith devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]); 99953e2a20eSAnsuel Smith if (IS_ERR(priv->rf[TSENS_SW_RST])) { 100053e2a20eSAnsuel Smith ret = PTR_ERR(priv->rf[TSENS_SW_RST]); 100153e2a20eSAnsuel Smith goto err_put_device; 100253e2a20eSAnsuel Smith } 100353e2a20eSAnsuel Smith 100453e2a20eSAnsuel Smith priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]); 100553e2a20eSAnsuel Smith if (IS_ERR(priv->rf[TRDY])) { 100653e2a20eSAnsuel Smith ret = PTR_ERR(priv->rf[TRDY]); 100753e2a20eSAnsuel Smith goto err_put_device; 100853e2a20eSAnsuel Smith } 100953e2a20eSAnsuel Smith 1010a7ff8297SAmit Kucheria /* This loop might need changes if enum regfield_ids is reordered */ 1011a7ff8297SAmit Kucheria for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { 1012a7ff8297SAmit Kucheria for (i = 0; i < priv->feat->max_sensors; i++) { 1013a7ff8297SAmit Kucheria int idx = j + i; 1014a7ff8297SAmit Kucheria 1015a7ff8297SAmit Kucheria priv->rf[idx] = devm_regmap_field_alloc(dev, 1016a7ff8297SAmit Kucheria priv->tm_map, 1017a7ff8297SAmit Kucheria priv->fields[idx]); 1018a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[idx])) { 1019a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[idx]); 1020a7ff8297SAmit Kucheria goto err_put_device; 1021a7ff8297SAmit Kucheria } 1022a7ff8297SAmit Kucheria } 1023a7ff8297SAmit Kucheria } 1024a7ff8297SAmit Kucheria 102553e2a20eSAnsuel Smith if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) { 1026a7ff8297SAmit Kucheria /* Loop might need changes if enum regfield_ids is reordered */ 1027a7ff8297SAmit Kucheria for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { 1028a7ff8297SAmit Kucheria for (i = 0; i < priv->feat->max_sensors; i++) { 1029a7ff8297SAmit Kucheria int idx = j + i; 1030a7ff8297SAmit Kucheria 1031a7ff8297SAmit Kucheria priv->rf[idx] = 1032a7ff8297SAmit Kucheria devm_regmap_field_alloc(dev, 1033a7ff8297SAmit Kucheria priv->tm_map, 1034a7ff8297SAmit Kucheria priv->fields[idx]); 1035a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[idx])) { 1036a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[idx]); 1037a7ff8297SAmit Kucheria goto err_put_device; 1038a7ff8297SAmit Kucheria } 1039a7ff8297SAmit Kucheria } 1040a7ff8297SAmit Kucheria } 1041a7ff8297SAmit Kucheria } 1042a7ff8297SAmit Kucheria 1043a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X && ver_minor > 2) { 1044a7ff8297SAmit Kucheria /* Watchdog is present only on v2.3+ */ 1045a7ff8297SAmit Kucheria priv->feat->has_watchdog = 1; 1046a7ff8297SAmit Kucheria for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) { 1047a7ff8297SAmit Kucheria priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map, 1048a7ff8297SAmit Kucheria priv->fields[i]); 1049a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[i])) { 1050a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[i]); 1051a7ff8297SAmit Kucheria goto err_put_device; 1052a7ff8297SAmit Kucheria } 1053a7ff8297SAmit Kucheria } 1054a7ff8297SAmit Kucheria /* 1055a7ff8297SAmit Kucheria * Watchdog is already enabled, unmask the bark. 1056a7ff8297SAmit Kucheria * Disable cycle completion monitoring 1057a7ff8297SAmit Kucheria */ 1058a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); 1059a7ff8297SAmit Kucheria regmap_field_write(priv->rf[CC_MON_MASK], 1); 1060a7ff8297SAmit Kucheria } 1061a7ff8297SAmit Kucheria 1062a7ff8297SAmit Kucheria spin_lock_init(&priv->ul_lock); 106353e2a20eSAnsuel Smith 106453e2a20eSAnsuel Smith /* VER_0 interrupt doesn't need to be enabled */ 106553e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1) 1066a7ff8297SAmit Kucheria tsens_enable_irq(priv); 106753e2a20eSAnsuel Smith 1068a7ff8297SAmit Kucheria err_put_device: 1069a7ff8297SAmit Kucheria put_device(&op->dev); 1070a7ff8297SAmit Kucheria return ret; 1071a7ff8297SAmit Kucheria } 1072a7ff8297SAmit Kucheria 1073ca1b9a9eSDaniel Lezcano static int tsens_get_temp(struct thermal_zone_device *tz, int *temp) 10749066073cSRajendra Nayak { 10755f68d078SDaniel Lezcano struct tsens_sensor *s = thermal_zone_device_priv(tz); 107669b628acSAmit Kucheria struct tsens_priv *priv = s->priv; 10779066073cSRajendra Nayak 10788b71bce4SAmit Kucheria return priv->ops->get_temp(s, temp); 10799066073cSRajendra Nayak } 10809066073cSRajendra Nayak 10815b97469aSArnd Bergmann static int __maybe_unused tsens_suspend(struct device *dev) 10829066073cSRajendra Nayak { 108369b628acSAmit Kucheria struct tsens_priv *priv = dev_get_drvdata(dev); 10849066073cSRajendra Nayak 108569b628acSAmit Kucheria if (priv->ops && priv->ops->suspend) 108669b628acSAmit Kucheria return priv->ops->suspend(priv); 10879066073cSRajendra Nayak 10889066073cSRajendra Nayak return 0; 10899066073cSRajendra Nayak } 10909066073cSRajendra Nayak 10915b97469aSArnd Bergmann static int __maybe_unused tsens_resume(struct device *dev) 10929066073cSRajendra Nayak { 109369b628acSAmit Kucheria struct tsens_priv *priv = dev_get_drvdata(dev); 10949066073cSRajendra Nayak 109569b628acSAmit Kucheria if (priv->ops && priv->ops->resume) 109669b628acSAmit Kucheria return priv->ops->resume(priv); 10979066073cSRajendra Nayak 10989066073cSRajendra Nayak return 0; 10999066073cSRajendra Nayak } 11009066073cSRajendra Nayak 11019066073cSRajendra Nayak static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); 11029066073cSRajendra Nayak 11039066073cSRajendra Nayak static const struct of_device_id tsens_table[] = { 11049066073cSRajendra Nayak { 11056b3aeafbSAnsuel Smith .compatible = "qcom,ipq8064-tsens", 11066b3aeafbSAnsuel Smith .data = &data_8960, 11076b3aeafbSAnsuel Smith }, { 11086840455dSRobert Marko .compatible = "qcom,ipq8074-tsens", 11096840455dSRobert Marko .data = &data_ipq8074, 11106840455dSRobert Marko }, { 1111a2149ab8SKonrad Dybcio .compatible = "qcom,mdm9607-tsens", 1112a2149ab8SKonrad Dybcio .data = &data_9607, 1113a2149ab8SKonrad Dybcio }, { 1114598e1afcSMatti Lehtimäki .compatible = "qcom,msm8226-tsens", 1115598e1afcSMatti Lehtimäki .data = &data_8226, 1116598e1afcSMatti Lehtimäki }, { 11174af164c1SStephan Gerhold .compatible = "qcom,msm8909-tsens", 11184af164c1SStephan Gerhold .data = &data_8909, 11194af164c1SStephan Gerhold }, { 11209066073cSRajendra Nayak .compatible = "qcom,msm8916-tsens", 1121840a5bd3SRajendra Nayak .data = &data_8916, 11229066073cSRajendra Nayak }, { 1123332bc8ebSShawn Guo .compatible = "qcom,msm8939-tsens", 1124332bc8ebSShawn Guo .data = &data_8939, 1125332bc8ebSShawn Guo }, { 1126a7d3006bSDmitry Baryshkov .compatible = "qcom,msm8956-tsens", 1127a7d3006bSDmitry Baryshkov .data = &data_8956, 1128a7d3006bSDmitry Baryshkov }, { 11292caf7396SDmitry Baryshkov .compatible = "qcom,msm8960-tsens", 11302caf7396SDmitry Baryshkov .data = &data_8960, 11312caf7396SDmitry Baryshkov }, { 11329066073cSRajendra Nayak .compatible = "qcom,msm8974-tsens", 11335e6703bdSRajendra Nayak .data = &data_8974, 1134d059c739SRajendra Nayak }, { 11350e580290SAngeloGioacchino Del Regno .compatible = "qcom,msm8976-tsens", 11360e580290SAngeloGioacchino Del Regno .data = &data_8976, 11370e580290SAngeloGioacchino Del Regno }, { 1138d059c739SRajendra Nayak .compatible = "qcom,msm8996-tsens", 1139d059c739SRajendra Nayak .data = &data_8996, 1140191dc74bSAmit Kucheria }, { 1141e8c24c6fSAmit Kucheria .compatible = "qcom,tsens-v1", 1142e8c24c6fSAmit Kucheria .data = &data_tsens_v1, 1143e8c24c6fSAmit Kucheria }, { 1144191dc74bSAmit Kucheria .compatible = "qcom,tsens-v2", 1145191dc74bSAmit Kucheria .data = &data_tsens_v2, 11469066073cSRajendra Nayak }, 11479066073cSRajendra Nayak {} 11489066073cSRajendra Nayak }; 11499066073cSRajendra Nayak MODULE_DEVICE_TABLE(of, tsens_table); 11509066073cSRajendra Nayak 1151ca1b9a9eSDaniel Lezcano static const struct thermal_zone_device_ops tsens_of_ops = { 11529066073cSRajendra Nayak .get_temp = tsens_get_temp, 1153634e11d5SAmit Kucheria .set_trips = tsens_set_trips, 11549066073cSRajendra Nayak }; 11559066073cSRajendra Nayak 115679125e03SAmit Kucheria static int tsens_register_irq(struct tsens_priv *priv, char *irqname, 115779125e03SAmit Kucheria irq_handler_t thread_fn) 115879125e03SAmit Kucheria { 115979125e03SAmit Kucheria struct platform_device *pdev; 116079125e03SAmit Kucheria int ret, irq; 116179125e03SAmit Kucheria 116279125e03SAmit Kucheria pdev = of_find_device_by_node(priv->dev->of_node); 116379125e03SAmit Kucheria if (!pdev) 116479125e03SAmit Kucheria return -ENODEV; 116579125e03SAmit Kucheria 116679125e03SAmit Kucheria irq = platform_get_irq_byname(pdev, irqname); 116779125e03SAmit Kucheria if (irq < 0) { 116879125e03SAmit Kucheria ret = irq; 116979125e03SAmit Kucheria /* For old DTs with no IRQ defined */ 117079125e03SAmit Kucheria if (irq == -ENXIO) 117179125e03SAmit Kucheria ret = 0; 117279125e03SAmit Kucheria } else { 117353e2a20eSAnsuel Smith /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */ 117453e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) 117579125e03SAmit Kucheria ret = devm_request_threaded_irq(&pdev->dev, irq, 117653e2a20eSAnsuel Smith thread_fn, NULL, 117753e2a20eSAnsuel Smith IRQF_TRIGGER_RISING, 117853e2a20eSAnsuel Smith dev_name(&pdev->dev), 117953e2a20eSAnsuel Smith priv); 118053e2a20eSAnsuel Smith else 118153e2a20eSAnsuel Smith ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 118253e2a20eSAnsuel Smith thread_fn, IRQF_ONESHOT, 118353e2a20eSAnsuel Smith dev_name(&pdev->dev), 118453e2a20eSAnsuel Smith priv); 118553e2a20eSAnsuel Smith 118679125e03SAmit Kucheria if (ret) 118779125e03SAmit Kucheria dev_err(&pdev->dev, "%s: failed to get irq\n", 118879125e03SAmit Kucheria __func__); 118979125e03SAmit Kucheria else 119079125e03SAmit Kucheria enable_irq_wake(irq); 119179125e03SAmit Kucheria } 119279125e03SAmit Kucheria 119379125e03SAmit Kucheria put_device(&pdev->dev); 119479125e03SAmit Kucheria return ret; 119579125e03SAmit Kucheria } 119679125e03SAmit Kucheria 1197*34b9a92bSPriyansh Jain #ifdef CONFIG_SUSPEND 1198*34b9a92bSPriyansh Jain static int tsens_reinit(struct tsens_priv *priv) 1199*34b9a92bSPriyansh Jain { 1200*34b9a92bSPriyansh Jain if (tsens_version(priv) >= VER_2_X) { 1201*34b9a92bSPriyansh Jain /* 1202*34b9a92bSPriyansh Jain * Re-enable the watchdog, unmask the bark. 1203*34b9a92bSPriyansh Jain * Disable cycle completion monitoring 1204*34b9a92bSPriyansh Jain */ 1205*34b9a92bSPriyansh Jain if (priv->feat->has_watchdog) { 1206*34b9a92bSPriyansh Jain regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); 1207*34b9a92bSPriyansh Jain regmap_field_write(priv->rf[CC_MON_MASK], 1); 1208*34b9a92bSPriyansh Jain } 1209*34b9a92bSPriyansh Jain 1210*34b9a92bSPriyansh Jain /* Re-enable interrupts */ 1211*34b9a92bSPriyansh Jain tsens_enable_irq(priv); 1212*34b9a92bSPriyansh Jain } 1213*34b9a92bSPriyansh Jain 1214*34b9a92bSPriyansh Jain return 0; 1215*34b9a92bSPriyansh Jain } 1216*34b9a92bSPriyansh Jain 1217*34b9a92bSPriyansh Jain int tsens_resume_common(struct tsens_priv *priv) 1218*34b9a92bSPriyansh Jain { 1219*34b9a92bSPriyansh Jain if (pm_suspend_target_state == PM_SUSPEND_MEM) 1220*34b9a92bSPriyansh Jain tsens_reinit(priv); 1221*34b9a92bSPriyansh Jain 1222*34b9a92bSPriyansh Jain return 0; 1223*34b9a92bSPriyansh Jain } 1224*34b9a92bSPriyansh Jain 1225*34b9a92bSPriyansh Jain #endif /* !CONFIG_SUSPEND */ 1226*34b9a92bSPriyansh Jain 122769b628acSAmit Kucheria static int tsens_register(struct tsens_priv *priv) 12289066073cSRajendra Nayak { 122979125e03SAmit Kucheria int i, ret; 12309066073cSRajendra Nayak struct thermal_zone_device *tzd; 12319066073cSRajendra Nayak 123269b628acSAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 123369b628acSAmit Kucheria priv->sensor[i].priv = priv; 1234ca1b9a9eSDaniel Lezcano tzd = devm_thermal_of_zone_register(priv->dev, priv->sensor[i].hw_id, 123569b628acSAmit Kucheria &priv->sensor[i], 12369066073cSRajendra Nayak &tsens_of_ops); 12379066073cSRajendra Nayak if (IS_ERR(tzd)) 12389066073cSRajendra Nayak continue; 123969b628acSAmit Kucheria priv->sensor[i].tzd = tzd; 124069b628acSAmit Kucheria if (priv->ops->enable) 124169b628acSAmit Kucheria priv->ops->enable(priv, i); 12428556e19dSDmitry Baryshkov 12437adbbb3bSYangtao Li devm_thermal_add_hwmon_sysfs(priv->dev, tzd); 12449066073cSRajendra Nayak } 1245634e11d5SAmit Kucheria 124653e2a20eSAnsuel Smith /* VER_0 require to set MIN and MAX THRESH 124753e2a20eSAnsuel Smith * These 2 regs are set using the: 124853e2a20eSAnsuel Smith * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C 124953e2a20eSAnsuel Smith * - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C 125053e2a20eSAnsuel Smith */ 125153e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) { 125253e2a20eSAnsuel Smith regmap_field_write(priv->rf[CRIT_THRESH_0], 125353e2a20eSAnsuel Smith tsens_mC_to_hw(priv->sensor, 120000)); 125453e2a20eSAnsuel Smith 125553e2a20eSAnsuel Smith regmap_field_write(priv->rf[CRIT_THRESH_1], 125653e2a20eSAnsuel Smith tsens_mC_to_hw(priv->sensor, 0)); 125753e2a20eSAnsuel Smith } 125853e2a20eSAnsuel Smith 12594360af35SRobert Marko if (priv->feat->combo_int) { 12604360af35SRobert Marko ret = tsens_register_irq(priv, "combined", 12614360af35SRobert Marko tsens_combined_irq_thread); 12624360af35SRobert Marko } else { 126379125e03SAmit Kucheria ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); 126479125e03SAmit Kucheria if (ret < 0) 126579125e03SAmit Kucheria return ret; 1266634e11d5SAmit Kucheria 126779125e03SAmit Kucheria if (priv->feat->crit_int) 126879125e03SAmit Kucheria ret = tsens_register_irq(priv, "critical", 126979125e03SAmit Kucheria tsens_critical_irq_thread); 12704360af35SRobert Marko } 1271634e11d5SAmit Kucheria 1272634e11d5SAmit Kucheria return ret; 12739066073cSRajendra Nayak } 12749066073cSRajendra Nayak 12759066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev) 12769066073cSRajendra Nayak { 12779066073cSRajendra Nayak int ret, i; 12789066073cSRajendra Nayak struct device *dev; 12799066073cSRajendra Nayak struct device_node *np; 128069b628acSAmit Kucheria struct tsens_priv *priv; 12813c040ce0SAmit Kucheria const struct tsens_plat_data *data; 12829066073cSRajendra Nayak const struct of_device_id *id; 12836d7c70d1SBjorn Andersson u32 num_sensors; 12849066073cSRajendra Nayak 12859066073cSRajendra Nayak if (pdev->dev.of_node) 12869066073cSRajendra Nayak dev = &pdev->dev; 12879066073cSRajendra Nayak else 12889066073cSRajendra Nayak dev = pdev->dev.parent; 12899066073cSRajendra Nayak 12909066073cSRajendra Nayak np = dev->of_node; 12919066073cSRajendra Nayak 12929066073cSRajendra Nayak id = of_match_node(tsens_table, np); 129320d4fd84SRajendra Nayak if (id) 12949066073cSRajendra Nayak data = id->data; 129520d4fd84SRajendra Nayak else 129620d4fd84SRajendra Nayak data = &data_8960; 12979066073cSRajendra Nayak 12986d7c70d1SBjorn Andersson num_sensors = data->num_sensors; 12996d7c70d1SBjorn Andersson 13006d7c70d1SBjorn Andersson if (np) 13016d7c70d1SBjorn Andersson of_property_read_u32(np, "#qcom,sensors", &num_sensors); 13026d7c70d1SBjorn Andersson 13036d7c70d1SBjorn Andersson if (num_sensors <= 0) { 13043795ad5eSAmit Kucheria dev_err(dev, "%s: invalid number of sensors\n", __func__); 13059066073cSRajendra Nayak return -EINVAL; 13069066073cSRajendra Nayak } 13079066073cSRajendra Nayak 130869b628acSAmit Kucheria priv = devm_kzalloc(dev, 130969b628acSAmit Kucheria struct_size(priv, sensor, num_sensors), 13100ed2dd03SKees Cook GFP_KERNEL); 131169b628acSAmit Kucheria if (!priv) 13129066073cSRajendra Nayak return -ENOMEM; 13139066073cSRajendra Nayak 131469b628acSAmit Kucheria priv->dev = dev; 131569b628acSAmit Kucheria priv->num_sensors = num_sensors; 131669b628acSAmit Kucheria priv->ops = data->ops; 131769b628acSAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 13189066073cSRajendra Nayak if (data->hw_ids) 131969b628acSAmit Kucheria priv->sensor[i].hw_id = data->hw_ids[i]; 13209066073cSRajendra Nayak else 132169b628acSAmit Kucheria priv->sensor[i].hw_id = i; 13229066073cSRajendra Nayak } 1323c1997054SAmit Kucheria priv->feat = data->feat; 1324c1997054SAmit Kucheria priv->fields = data->fields; 13259066073cSRajendra Nayak 13260e9c0bc7SAmit Kucheria platform_set_drvdata(pdev, priv); 13270e9c0bc7SAmit Kucheria 132869b628acSAmit Kucheria if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) 13299066073cSRajendra Nayak return -EINVAL; 13309066073cSRajendra Nayak 133169b628acSAmit Kucheria ret = priv->ops->init(priv); 13329066073cSRajendra Nayak if (ret < 0) { 13333795ad5eSAmit Kucheria dev_err(dev, "%s: init failed\n", __func__); 13349066073cSRajendra Nayak return ret; 13359066073cSRajendra Nayak } 13369066073cSRajendra Nayak 133769b628acSAmit Kucheria if (priv->ops->calibrate) { 133869b628acSAmit Kucheria ret = priv->ops->calibrate(priv); 13399066073cSRajendra Nayak if (ret < 0) { 1340fc7d18cfSAmit Kucheria if (ret != -EPROBE_DEFER) 13413795ad5eSAmit Kucheria dev_err(dev, "%s: calibration failed\n", __func__); 13429066073cSRajendra Nayak return ret; 13439066073cSRajendra Nayak } 13449066073cSRajendra Nayak } 13459066073cSRajendra Nayak 1346de48d876SChristian Marangi ret = tsens_register(priv); 1347de48d876SChristian Marangi if (!ret) 1348de48d876SChristian Marangi tsens_debug_init(pdev); 1349de48d876SChristian Marangi 1350de48d876SChristian Marangi return ret; 13519066073cSRajendra Nayak } 13529066073cSRajendra Nayak 13532128ba46SUwe Kleine-König static void tsens_remove(struct platform_device *pdev) 13549066073cSRajendra Nayak { 135569b628acSAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 13569066073cSRajendra Nayak 13577c938f48SAmit Kucheria debugfs_remove_recursive(priv->debug_root); 1358634e11d5SAmit Kucheria tsens_disable_irq(priv); 135969b628acSAmit Kucheria if (priv->ops->disable) 136069b628acSAmit Kucheria priv->ops->disable(priv); 13619066073cSRajendra Nayak } 13629066073cSRajendra Nayak 13639066073cSRajendra Nayak static struct platform_driver tsens_driver = { 13649066073cSRajendra Nayak .probe = tsens_probe, 13652128ba46SUwe Kleine-König .remove_new = tsens_remove, 13669066073cSRajendra Nayak .driver = { 13679066073cSRajendra Nayak .name = "qcom-tsens", 13689066073cSRajendra Nayak .pm = &tsens_pm_ops, 13699066073cSRajendra Nayak .of_match_table = tsens_table, 13709066073cSRajendra Nayak }, 13719066073cSRajendra Nayak }; 13729066073cSRajendra Nayak module_platform_driver(tsens_driver); 13739066073cSRajendra Nayak 13749066073cSRajendra Nayak MODULE_LICENSE("GPL v2"); 13759066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); 13769066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens"); 1377