xref: /linux/drivers/thermal/qcom/tsens.c (revision d998ddc8)
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>
2034b9a92bSPriyansh 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 
qfprom_read(struct device * dev,const char * cname)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 
tsens_read_calibration(struct tsens_priv * priv,int shift,u32 * p1,u32 * p2,bool backup)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 
tsens_calibrate_nvmem(struct tsens_priv * priv,int shift)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 
tsens_calibrate_common(struct tsens_priv * priv)184498d2457SDmitry Baryshkov int tsens_calibrate_common(struct tsens_priv *priv)
185498d2457SDmitry Baryshkov {
186498d2457SDmitry Baryshkov 	return tsens_calibrate_nvmem(priv, 2);
187498d2457SDmitry Baryshkov }
188498d2457SDmitry Baryshkov 
tsens_read_cell(const struct tsens_single_value * cell,u8 len,u32 * data0,u32 * data1)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 
tsens_read_calibration_legacy(struct tsens_priv * priv,const struct tsens_legacy_calibration_format * format,u32 * p1,u32 * p2,u32 * cdata0,u32 * cdata1)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  */
compute_intercept_slope(struct tsens_priv * priv,u32 * p1,u32 * p2,u32 mode)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",
268*d998ddc8SAleksandr Mishin 			__func__, i, p1[i], p2 ? p2[i] : 0);
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 
degc_to_code(int degc,const struct tsens_sensor * s)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 
code_to_degc(u32 adc_code,const struct tsens_sensor * s)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  */
tsens_hw_to_mC(const struct tsens_sensor * s,int field)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  */
tsens_mC_to_hw(const struct tsens_sensor * s,int temp)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 
tsens_version(struct tsens_priv * priv)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 
tsens_set_interrupt_v1(struct tsens_priv * priv,u32 hw_id,enum tsens_irq_type irq_type,bool enable)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 
tsens_set_interrupt_v2(struct tsens_priv * priv,u32 hw_id,enum tsens_irq_type irq_type,bool enable)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  */
tsens_set_interrupt(struct tsens_priv * priv,u32 hw_id,enum tsens_irq_type irq_type,bool enable)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  */
tsens_threshold_violated(struct tsens_priv * priv,u32 hw_id,struct tsens_irq_data * d)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 
tsens_read_irq_state(struct tsens_priv * priv,u32 hw_id,const struct tsens_sensor * s,struct tsens_irq_data * d)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 
masked_irq(u32 hw_id,u32 mask,enum tsens_ver ver)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  */
tsens_critical_irq_thread(int irq,void * data)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  */
tsens_irq_thread(int irq,void * data)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  */
tsens_combined_irq_thread(int irq,void * data)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 
tsens_set_trips(struct thermal_zone_device * tz,int low,int high)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 
tsens_enable_irq(struct tsens_priv * priv)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 
tsens_disable_irq(struct tsens_priv * priv)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 
get_temp_tsens_valid(const struct tsens_sensor * s,int * temp)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 
get_temp_common(const struct tsens_sensor * s,int * temp)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
dbg_sensors_show(struct seq_file * s,void * data)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 
dbg_version_show(struct seq_file * s,void * data)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 
tsens_debug_init(struct platform_device * pdev)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
tsens_debug_init(struct platform_device * pdev)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 
init_common(struct tsens_priv * priv)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 
tsens_get_temp(struct thermal_zone_device * tz,int * temp)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 
tsens_suspend(struct device * dev)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 
tsens_resume(struct device * dev)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 
tsens_register_irq(struct tsens_priv * priv,char * irqname,irq_handler_t thread_fn)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 
119734b9a92bSPriyansh Jain #ifdef CONFIG_SUSPEND
tsens_reinit(struct tsens_priv * priv)119834b9a92bSPriyansh Jain static int tsens_reinit(struct tsens_priv *priv)
119934b9a92bSPriyansh Jain {
120034b9a92bSPriyansh Jain 	if (tsens_version(priv) >= VER_2_X) {
120134b9a92bSPriyansh Jain 		/*
120234b9a92bSPriyansh Jain 		 * Re-enable the watchdog, unmask the bark.
120334b9a92bSPriyansh Jain 		 * Disable cycle completion monitoring
120434b9a92bSPriyansh Jain 		 */
120534b9a92bSPriyansh Jain 		if (priv->feat->has_watchdog) {
120634b9a92bSPriyansh Jain 			regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
120734b9a92bSPriyansh Jain 			regmap_field_write(priv->rf[CC_MON_MASK], 1);
120834b9a92bSPriyansh Jain 		}
120934b9a92bSPriyansh Jain 
121034b9a92bSPriyansh Jain 		/* Re-enable interrupts */
121134b9a92bSPriyansh Jain 		tsens_enable_irq(priv);
121234b9a92bSPriyansh Jain 	}
121334b9a92bSPriyansh Jain 
121434b9a92bSPriyansh Jain 	return 0;
121534b9a92bSPriyansh Jain }
121634b9a92bSPriyansh Jain 
tsens_resume_common(struct tsens_priv * priv)121734b9a92bSPriyansh Jain int tsens_resume_common(struct tsens_priv *priv)
121834b9a92bSPriyansh Jain {
121934b9a92bSPriyansh Jain 	if (pm_suspend_target_state == PM_SUSPEND_MEM)
122034b9a92bSPriyansh Jain 		tsens_reinit(priv);
122134b9a92bSPriyansh Jain 
122234b9a92bSPriyansh Jain 	return 0;
122334b9a92bSPriyansh Jain }
122434b9a92bSPriyansh Jain 
122534b9a92bSPriyansh Jain #endif /* !CONFIG_SUSPEND */
122634b9a92bSPriyansh Jain 
tsens_register(struct tsens_priv * priv)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 
tsens_probe(struct platform_device * pdev)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 
tsens_remove(struct platform_device * pdev)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