xref: /linux/drivers/thermal/qcom/tsens.c (revision cf96921876dcee4d6ac07b9de470368a075ba9ad)
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>
209066073cSRajendra Nayak #include <linux/thermal.h>
219066073cSRajendra Nayak #include "tsens.h"
229066073cSRajendra Nayak 
23a7ff8297SAmit Kucheria /**
24a7ff8297SAmit Kucheria  * struct tsens_irq_data - IRQ status and temperature violations
25a7ff8297SAmit Kucheria  * @up_viol:        upper threshold violated
26a7ff8297SAmit Kucheria  * @up_thresh:      upper threshold temperature value
27a7ff8297SAmit Kucheria  * @up_irq_mask:    mask register for upper threshold irqs
28a7ff8297SAmit Kucheria  * @up_irq_clear:   clear register for uppper threshold irqs
29a7ff8297SAmit Kucheria  * @low_viol:       lower threshold violated
30a7ff8297SAmit Kucheria  * @low_thresh:     lower threshold temperature value
31a7ff8297SAmit Kucheria  * @low_irq_mask:   mask register for lower threshold irqs
32a7ff8297SAmit Kucheria  * @low_irq_clear:  clear register for lower threshold irqs
33a7ff8297SAmit Kucheria  * @crit_viol:      critical threshold violated
34a7ff8297SAmit Kucheria  * @crit_thresh:    critical threshold temperature value
35a7ff8297SAmit Kucheria  * @crit_irq_mask:  mask register for critical threshold irqs
36a7ff8297SAmit Kucheria  * @crit_irq_clear: clear register for critical threshold irqs
37a7ff8297SAmit Kucheria  *
38a7ff8297SAmit Kucheria  * Structure containing data about temperature threshold settings and
39a7ff8297SAmit Kucheria  * irq status if they were violated.
40a7ff8297SAmit Kucheria  */
41a7ff8297SAmit Kucheria struct tsens_irq_data {
42a7ff8297SAmit Kucheria 	u32 up_viol;
43a7ff8297SAmit Kucheria 	int up_thresh;
44a7ff8297SAmit Kucheria 	u32 up_irq_mask;
45a7ff8297SAmit Kucheria 	u32 up_irq_clear;
46a7ff8297SAmit Kucheria 	u32 low_viol;
47a7ff8297SAmit Kucheria 	int low_thresh;
48a7ff8297SAmit Kucheria 	u32 low_irq_mask;
49a7ff8297SAmit Kucheria 	u32 low_irq_clear;
50a7ff8297SAmit Kucheria 	u32 crit_viol;
51a7ff8297SAmit Kucheria 	u32 crit_thresh;
52a7ff8297SAmit Kucheria 	u32 crit_irq_mask;
53a7ff8297SAmit Kucheria 	u32 crit_irq_clear;
54a7ff8297SAmit Kucheria };
55a7ff8297SAmit Kucheria 
56a7ff8297SAmit Kucheria char *qfprom_read(struct device *dev, const char *cname)
57a7ff8297SAmit Kucheria {
58a7ff8297SAmit Kucheria 	struct nvmem_cell *cell;
59a7ff8297SAmit Kucheria 	ssize_t data;
60a7ff8297SAmit Kucheria 	char *ret;
61a7ff8297SAmit Kucheria 
62a7ff8297SAmit Kucheria 	cell = nvmem_cell_get(dev, cname);
63a7ff8297SAmit Kucheria 	if (IS_ERR(cell))
64a7ff8297SAmit Kucheria 		return ERR_CAST(cell);
65a7ff8297SAmit Kucheria 
66a7ff8297SAmit Kucheria 	ret = nvmem_cell_read(cell, &data);
67a7ff8297SAmit Kucheria 	nvmem_cell_put(cell);
68a7ff8297SAmit Kucheria 
69a7ff8297SAmit Kucheria 	return ret;
70a7ff8297SAmit Kucheria }
71a7ff8297SAmit Kucheria 
72a7ff8297SAmit Kucheria /*
73a7ff8297SAmit Kucheria  * Use this function on devices where slope and offset calculations
74a7ff8297SAmit Kucheria  * depend on calibration data read from qfprom. On others the slope
75a7ff8297SAmit Kucheria  * and offset values are derived from tz->tzp->slope and tz->tzp->offset
76a7ff8297SAmit Kucheria  * resp.
77a7ff8297SAmit Kucheria  */
78a7ff8297SAmit Kucheria void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
79a7ff8297SAmit Kucheria 			     u32 *p2, u32 mode)
80a7ff8297SAmit Kucheria {
81a7ff8297SAmit Kucheria 	int i;
82a7ff8297SAmit Kucheria 	int num, den;
83a7ff8297SAmit Kucheria 
84a7ff8297SAmit Kucheria 	for (i = 0; i < priv->num_sensors; i++) {
85a7ff8297SAmit Kucheria 		dev_dbg(priv->dev,
86a7ff8297SAmit Kucheria 			"%s: sensor%d - data_point1:%#x data_point2:%#x\n",
87a7ff8297SAmit Kucheria 			__func__, i, p1[i], p2[i]);
88a7ff8297SAmit Kucheria 
899d51769bSAnsuel Smith 		if (!priv->sensor[i].slope)
90a7ff8297SAmit Kucheria 			priv->sensor[i].slope = SLOPE_DEFAULT;
91a7ff8297SAmit Kucheria 		if (mode == TWO_PT_CALIB) {
92a7ff8297SAmit Kucheria 			/*
93a7ff8297SAmit Kucheria 			 * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
94a7ff8297SAmit Kucheria 			 *	temp_120_degc - temp_30_degc (x2 - x1)
95a7ff8297SAmit Kucheria 			 */
96a7ff8297SAmit Kucheria 			num = p2[i] - p1[i];
97a7ff8297SAmit Kucheria 			num *= SLOPE_FACTOR;
98a7ff8297SAmit Kucheria 			den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
99a7ff8297SAmit Kucheria 			priv->sensor[i].slope = num / den;
100a7ff8297SAmit Kucheria 		}
101a7ff8297SAmit Kucheria 
102a7ff8297SAmit Kucheria 		priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
103a7ff8297SAmit Kucheria 				(CAL_DEGC_PT1 *
104a7ff8297SAmit Kucheria 				priv->sensor[i].slope);
105a7ff8297SAmit Kucheria 		dev_dbg(priv->dev, "%s: offset:%d\n", __func__,
106a7ff8297SAmit Kucheria 			priv->sensor[i].offset);
107a7ff8297SAmit Kucheria 	}
108a7ff8297SAmit Kucheria }
109a7ff8297SAmit Kucheria 
110a7ff8297SAmit Kucheria static inline u32 degc_to_code(int degc, const struct tsens_sensor *s)
111a7ff8297SAmit Kucheria {
112a7ff8297SAmit Kucheria 	u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR);
113a7ff8297SAmit Kucheria 
114a7ff8297SAmit Kucheria 	pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc);
115a7ff8297SAmit Kucheria 	return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE);
116a7ff8297SAmit Kucheria }
117a7ff8297SAmit Kucheria 
118a7ff8297SAmit Kucheria static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
119a7ff8297SAmit Kucheria {
120a7ff8297SAmit Kucheria 	int degc, num, den;
121a7ff8297SAmit Kucheria 
122a7ff8297SAmit Kucheria 	num = (adc_code * SLOPE_FACTOR) - s->offset;
123a7ff8297SAmit Kucheria 	den = s->slope;
124a7ff8297SAmit Kucheria 
125a7ff8297SAmit Kucheria 	if (num > 0)
126a7ff8297SAmit Kucheria 		degc = num + (den / 2);
127a7ff8297SAmit Kucheria 	else if (num < 0)
128a7ff8297SAmit Kucheria 		degc = num - (den / 2);
129a7ff8297SAmit Kucheria 	else
130a7ff8297SAmit Kucheria 		degc = num;
131a7ff8297SAmit Kucheria 
132a7ff8297SAmit Kucheria 	degc /= den;
133a7ff8297SAmit Kucheria 
134a7ff8297SAmit Kucheria 	return degc;
135a7ff8297SAmit Kucheria }
136a7ff8297SAmit Kucheria 
137a7ff8297SAmit Kucheria /**
138a7ff8297SAmit Kucheria  * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
139a7ff8297SAmit Kucheria  * @s:     Pointer to sensor struct
140a7ff8297SAmit Kucheria  * @field: Index into regmap_field array pointing to temperature data
141a7ff8297SAmit Kucheria  *
142a7ff8297SAmit Kucheria  * This function handles temperature returned in ADC code or deciCelsius
143a7ff8297SAmit Kucheria  * depending on IP version.
144a7ff8297SAmit Kucheria  *
145a7ff8297SAmit Kucheria  * Return: Temperature in milliCelsius on success, a negative errno will
146a7ff8297SAmit Kucheria  * be returned in error cases
147a7ff8297SAmit Kucheria  */
148a7ff8297SAmit Kucheria static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
149a7ff8297SAmit Kucheria {
150a7ff8297SAmit Kucheria 	struct tsens_priv *priv = s->priv;
151a7ff8297SAmit Kucheria 	u32 resolution;
152a7ff8297SAmit Kucheria 	u32 temp = 0;
153a7ff8297SAmit Kucheria 	int ret;
154a7ff8297SAmit Kucheria 
155a7ff8297SAmit Kucheria 	resolution = priv->fields[LAST_TEMP_0].msb -
156a7ff8297SAmit Kucheria 		priv->fields[LAST_TEMP_0].lsb;
157a7ff8297SAmit Kucheria 
158a7ff8297SAmit Kucheria 	ret = regmap_field_read(priv->rf[field], &temp);
159a7ff8297SAmit Kucheria 	if (ret)
160a7ff8297SAmit Kucheria 		return ret;
161a7ff8297SAmit Kucheria 
162a7ff8297SAmit Kucheria 	/* Convert temperature from ADC code to milliCelsius */
163a7ff8297SAmit Kucheria 	if (priv->feat->adc)
164a7ff8297SAmit Kucheria 		return code_to_degc(temp, s) * 1000;
165a7ff8297SAmit Kucheria 
166a7ff8297SAmit Kucheria 	/* deciCelsius -> milliCelsius along with sign extension */
167a7ff8297SAmit Kucheria 	return sign_extend32(temp, resolution) * 100;
168a7ff8297SAmit Kucheria }
169a7ff8297SAmit Kucheria 
170a7ff8297SAmit Kucheria /**
171a7ff8297SAmit Kucheria  * tsens_mC_to_hw - Convert temperature to hardware register value
172a7ff8297SAmit Kucheria  * @s: Pointer to sensor struct
173a7ff8297SAmit Kucheria  * @temp: temperature in milliCelsius to be programmed to hardware
174a7ff8297SAmit Kucheria  *
175a7ff8297SAmit Kucheria  * This function outputs the value to be written to hardware in ADC code
176a7ff8297SAmit Kucheria  * or deciCelsius depending on IP version.
177a7ff8297SAmit Kucheria  *
178a7ff8297SAmit Kucheria  * Return: ADC code or temperature in deciCelsius.
179a7ff8297SAmit Kucheria  */
180a7ff8297SAmit Kucheria static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
181a7ff8297SAmit Kucheria {
182a7ff8297SAmit Kucheria 	struct tsens_priv *priv = s->priv;
183a7ff8297SAmit Kucheria 
184a7ff8297SAmit Kucheria 	/* milliC to adc code */
185a7ff8297SAmit Kucheria 	if (priv->feat->adc)
186a7ff8297SAmit Kucheria 		return degc_to_code(temp / 1000, s);
187a7ff8297SAmit Kucheria 
188a7ff8297SAmit Kucheria 	/* milliC to deciC */
189a7ff8297SAmit Kucheria 	return temp / 100;
190a7ff8297SAmit Kucheria }
191a7ff8297SAmit Kucheria 
192a7ff8297SAmit Kucheria static inline enum tsens_ver tsens_version(struct tsens_priv *priv)
193a7ff8297SAmit Kucheria {
194a7ff8297SAmit Kucheria 	return priv->feat->ver_major;
195a7ff8297SAmit Kucheria }
196a7ff8297SAmit Kucheria 
197a7ff8297SAmit Kucheria static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
198a7ff8297SAmit Kucheria 				   enum tsens_irq_type irq_type, bool enable)
199a7ff8297SAmit Kucheria {
200a7ff8297SAmit Kucheria 	u32 index = 0;
201a7ff8297SAmit Kucheria 
202a7ff8297SAmit Kucheria 	switch (irq_type) {
203a7ff8297SAmit Kucheria 	case UPPER:
204a7ff8297SAmit Kucheria 		index = UP_INT_CLEAR_0 + hw_id;
205a7ff8297SAmit Kucheria 		break;
206a7ff8297SAmit Kucheria 	case LOWER:
207a7ff8297SAmit Kucheria 		index = LOW_INT_CLEAR_0 + hw_id;
208a7ff8297SAmit Kucheria 		break;
209a7ff8297SAmit Kucheria 	case CRITICAL:
210a7ff8297SAmit Kucheria 		/* No critical interrupts before v2 */
211a7ff8297SAmit Kucheria 		return;
212a7ff8297SAmit Kucheria 	}
213a7ff8297SAmit Kucheria 	regmap_field_write(priv->rf[index], enable ? 0 : 1);
214a7ff8297SAmit Kucheria }
215a7ff8297SAmit Kucheria 
216a7ff8297SAmit Kucheria static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
217a7ff8297SAmit Kucheria 				   enum tsens_irq_type irq_type, bool enable)
218a7ff8297SAmit Kucheria {
219a7ff8297SAmit Kucheria 	u32 index_mask = 0, index_clear = 0;
220a7ff8297SAmit Kucheria 
221a7ff8297SAmit Kucheria 	/*
222a7ff8297SAmit Kucheria 	 * To enable the interrupt flag for a sensor:
223a7ff8297SAmit Kucheria 	 *    - clear the mask bit
224a7ff8297SAmit Kucheria 	 * To disable the interrupt flag for a sensor:
225a7ff8297SAmit Kucheria 	 *    - Mask further interrupts for this sensor
226a7ff8297SAmit Kucheria 	 *    - Write 1 followed by 0 to clear the interrupt
227a7ff8297SAmit Kucheria 	 */
228a7ff8297SAmit Kucheria 	switch (irq_type) {
229a7ff8297SAmit Kucheria 	case UPPER:
230a7ff8297SAmit Kucheria 		index_mask  = UP_INT_MASK_0 + hw_id;
231a7ff8297SAmit Kucheria 		index_clear = UP_INT_CLEAR_0 + hw_id;
232a7ff8297SAmit Kucheria 		break;
233a7ff8297SAmit Kucheria 	case LOWER:
234a7ff8297SAmit Kucheria 		index_mask  = LOW_INT_MASK_0 + hw_id;
235a7ff8297SAmit Kucheria 		index_clear = LOW_INT_CLEAR_0 + hw_id;
236a7ff8297SAmit Kucheria 		break;
237a7ff8297SAmit Kucheria 	case CRITICAL:
238a7ff8297SAmit Kucheria 		index_mask  = CRIT_INT_MASK_0 + hw_id;
239a7ff8297SAmit Kucheria 		index_clear = CRIT_INT_CLEAR_0 + hw_id;
240a7ff8297SAmit Kucheria 		break;
241a7ff8297SAmit Kucheria 	}
242a7ff8297SAmit Kucheria 
243a7ff8297SAmit Kucheria 	if (enable) {
244a7ff8297SAmit Kucheria 		regmap_field_write(priv->rf[index_mask], 0);
245a7ff8297SAmit Kucheria 	} else {
246a7ff8297SAmit Kucheria 		regmap_field_write(priv->rf[index_mask],  1);
247a7ff8297SAmit Kucheria 		regmap_field_write(priv->rf[index_clear], 1);
248a7ff8297SAmit Kucheria 		regmap_field_write(priv->rf[index_clear], 0);
249a7ff8297SAmit Kucheria 	}
250a7ff8297SAmit Kucheria }
251a7ff8297SAmit Kucheria 
252a7ff8297SAmit Kucheria /**
253a7ff8297SAmit Kucheria  * tsens_set_interrupt - Set state of an interrupt
254a7ff8297SAmit Kucheria  * @priv: Pointer to tsens controller private data
255a7ff8297SAmit Kucheria  * @hw_id: Hardware ID aka. sensor number
256a7ff8297SAmit Kucheria  * @irq_type: irq_type from enum tsens_irq_type
257a7ff8297SAmit Kucheria  * @enable: false = disable, true = enable
258a7ff8297SAmit Kucheria  *
259a7ff8297SAmit Kucheria  * Call IP-specific function to set state of an interrupt
260a7ff8297SAmit Kucheria  *
261a7ff8297SAmit Kucheria  * Return: void
262a7ff8297SAmit Kucheria  */
263a7ff8297SAmit Kucheria static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
264a7ff8297SAmit Kucheria 				enum tsens_irq_type irq_type, bool enable)
265a7ff8297SAmit Kucheria {
266a7ff8297SAmit Kucheria 	dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
267a7ff8297SAmit Kucheria 		irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
268a7ff8297SAmit Kucheria 		enable ? "en" : "dis");
269a7ff8297SAmit Kucheria 	if (tsens_version(priv) > VER_1_X)
270a7ff8297SAmit Kucheria 		tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
271a7ff8297SAmit Kucheria 	else
272a7ff8297SAmit Kucheria 		tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
273a7ff8297SAmit Kucheria }
274a7ff8297SAmit Kucheria 
275a7ff8297SAmit Kucheria /**
276a7ff8297SAmit Kucheria  * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
277a7ff8297SAmit Kucheria  * @priv: Pointer to tsens controller private data
278a7ff8297SAmit Kucheria  * @hw_id: Hardware ID aka. sensor number
279a7ff8297SAmit Kucheria  * @d: Pointer to irq state data
280a7ff8297SAmit Kucheria  *
281a7ff8297SAmit Kucheria  * Return: 0 if threshold was not violated, 1 if it was violated and negative
282a7ff8297SAmit Kucheria  * errno in case of errors
283a7ff8297SAmit Kucheria  */
284a7ff8297SAmit Kucheria static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
285a7ff8297SAmit Kucheria 				    struct tsens_irq_data *d)
286a7ff8297SAmit Kucheria {
287a7ff8297SAmit Kucheria 	int ret;
288a7ff8297SAmit Kucheria 
289a7ff8297SAmit Kucheria 	ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
290a7ff8297SAmit Kucheria 	if (ret)
291a7ff8297SAmit Kucheria 		return ret;
292a7ff8297SAmit Kucheria 	ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
293a7ff8297SAmit Kucheria 	if (ret)
294a7ff8297SAmit Kucheria 		return ret;
295a7ff8297SAmit Kucheria 
296a7ff8297SAmit Kucheria 	if (priv->feat->crit_int) {
297a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
298a7ff8297SAmit Kucheria 					&d->crit_viol);
299a7ff8297SAmit Kucheria 		if (ret)
300a7ff8297SAmit Kucheria 			return ret;
301a7ff8297SAmit Kucheria 	}
302a7ff8297SAmit Kucheria 
303a7ff8297SAmit Kucheria 	if (d->up_viol || d->low_viol || d->crit_viol)
304a7ff8297SAmit Kucheria 		return 1;
305a7ff8297SAmit Kucheria 
306a7ff8297SAmit Kucheria 	return 0;
307a7ff8297SAmit Kucheria }
308a7ff8297SAmit Kucheria 
309a7ff8297SAmit Kucheria static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
310a7ff8297SAmit Kucheria 				const struct tsens_sensor *s,
311a7ff8297SAmit Kucheria 				struct tsens_irq_data *d)
312a7ff8297SAmit Kucheria {
313a7ff8297SAmit Kucheria 	int ret;
314a7ff8297SAmit Kucheria 
315a7ff8297SAmit Kucheria 	ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
316a7ff8297SAmit Kucheria 	if (ret)
317a7ff8297SAmit Kucheria 		return ret;
318a7ff8297SAmit Kucheria 	ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
319a7ff8297SAmit Kucheria 	if (ret)
320a7ff8297SAmit Kucheria 		return ret;
321a7ff8297SAmit Kucheria 	if (tsens_version(priv) > VER_1_X) {
322a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
323a7ff8297SAmit Kucheria 		if (ret)
324a7ff8297SAmit Kucheria 			return ret;
325a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
326a7ff8297SAmit Kucheria 		if (ret)
327a7ff8297SAmit Kucheria 			return ret;
328a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
329a7ff8297SAmit Kucheria 					&d->crit_irq_clear);
330a7ff8297SAmit Kucheria 		if (ret)
331a7ff8297SAmit Kucheria 			return ret;
332a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
333a7ff8297SAmit Kucheria 					&d->crit_irq_mask);
334a7ff8297SAmit Kucheria 		if (ret)
335a7ff8297SAmit Kucheria 			return ret;
336a7ff8297SAmit Kucheria 
337a7ff8297SAmit Kucheria 		d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
338a7ff8297SAmit Kucheria 	} else {
339a7ff8297SAmit Kucheria 		/* No mask register on older TSENS */
340a7ff8297SAmit Kucheria 		d->up_irq_mask = 0;
341a7ff8297SAmit Kucheria 		d->low_irq_mask = 0;
342a7ff8297SAmit Kucheria 		d->crit_irq_clear = 0;
343a7ff8297SAmit Kucheria 		d->crit_irq_mask = 0;
344a7ff8297SAmit Kucheria 		d->crit_thresh = 0;
345a7ff8297SAmit Kucheria 	}
346a7ff8297SAmit Kucheria 
347a7ff8297SAmit Kucheria 	d->up_thresh  = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
348a7ff8297SAmit Kucheria 	d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
349a7ff8297SAmit Kucheria 
350a7ff8297SAmit Kucheria 	dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
351a7ff8297SAmit Kucheria 		hw_id, __func__,
352a7ff8297SAmit Kucheria 		(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
353a7ff8297SAmit Kucheria 		d->low_viol, d->up_viol, d->crit_viol,
354a7ff8297SAmit Kucheria 		d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
355a7ff8297SAmit Kucheria 		d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
356a7ff8297SAmit Kucheria 	dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
357a7ff8297SAmit Kucheria 		(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
358a7ff8297SAmit Kucheria 		d->low_thresh, d->up_thresh, d->crit_thresh);
359a7ff8297SAmit Kucheria 
360a7ff8297SAmit Kucheria 	return 0;
361a7ff8297SAmit Kucheria }
362a7ff8297SAmit Kucheria 
363a7ff8297SAmit Kucheria static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
364a7ff8297SAmit Kucheria {
365a7ff8297SAmit Kucheria 	if (ver > VER_1_X)
366a7ff8297SAmit Kucheria 		return mask & (1 << hw_id);
367a7ff8297SAmit Kucheria 
368a7ff8297SAmit Kucheria 	/* v1, v0.1 don't have a irq mask register */
369a7ff8297SAmit Kucheria 	return 0;
370a7ff8297SAmit Kucheria }
371a7ff8297SAmit Kucheria 
372a7ff8297SAmit Kucheria /**
373a7ff8297SAmit Kucheria  * tsens_critical_irq_thread() - Threaded handler for critical interrupts
374a7ff8297SAmit Kucheria  * @irq: irq number
375a7ff8297SAmit Kucheria  * @data: tsens controller private data
376a7ff8297SAmit Kucheria  *
377a7ff8297SAmit Kucheria  * Check FSM watchdog bark status and clear if needed.
378a7ff8297SAmit Kucheria  * Check all sensors to find ones that violated their critical threshold limits.
379a7ff8297SAmit Kucheria  * Clear and then re-enable the interrupt.
380a7ff8297SAmit Kucheria  *
381a7ff8297SAmit Kucheria  * The level-triggered interrupt might deassert if the temperature returned to
382a7ff8297SAmit Kucheria  * within the threshold limits by the time the handler got scheduled. We
383a7ff8297SAmit Kucheria  * consider the irq to have been handled in that case.
384a7ff8297SAmit Kucheria  *
385a7ff8297SAmit Kucheria  * Return: IRQ_HANDLED
386a7ff8297SAmit Kucheria  */
3873ecc8292SAmit Kucheria static irqreturn_t tsens_critical_irq_thread(int irq, void *data)
388a7ff8297SAmit Kucheria {
389a7ff8297SAmit Kucheria 	struct tsens_priv *priv = data;
390a7ff8297SAmit Kucheria 	struct tsens_irq_data d;
391a7ff8297SAmit Kucheria 	int temp, ret, i;
392a7ff8297SAmit Kucheria 	u32 wdog_status, wdog_count;
393a7ff8297SAmit Kucheria 
394a7ff8297SAmit Kucheria 	if (priv->feat->has_watchdog) {
395a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
396a7ff8297SAmit Kucheria 					&wdog_status);
397a7ff8297SAmit Kucheria 		if (ret)
398a7ff8297SAmit Kucheria 			return ret;
399a7ff8297SAmit Kucheria 
400a7ff8297SAmit Kucheria 		if (wdog_status) {
401a7ff8297SAmit Kucheria 			/* Clear WDOG interrupt */
402a7ff8297SAmit Kucheria 			regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
403a7ff8297SAmit Kucheria 			regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
404a7ff8297SAmit Kucheria 			ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
405a7ff8297SAmit Kucheria 						&wdog_count);
406a7ff8297SAmit Kucheria 			if (ret)
407a7ff8297SAmit Kucheria 				return ret;
408a7ff8297SAmit Kucheria 			if (wdog_count)
409a7ff8297SAmit Kucheria 				dev_dbg(priv->dev, "%s: watchdog count: %d\n",
410a7ff8297SAmit Kucheria 					__func__, wdog_count);
411a7ff8297SAmit Kucheria 
412a7ff8297SAmit Kucheria 			/* Fall through to handle critical interrupts if any */
413a7ff8297SAmit Kucheria 		}
414a7ff8297SAmit Kucheria 	}
415a7ff8297SAmit Kucheria 
416a7ff8297SAmit Kucheria 	for (i = 0; i < priv->num_sensors; i++) {
417a7ff8297SAmit Kucheria 		const struct tsens_sensor *s = &priv->sensor[i];
418a7ff8297SAmit Kucheria 		u32 hw_id = s->hw_id;
419a7ff8297SAmit Kucheria 
420*cf969218SAnsuel Smith 		if (!s->tzd)
421a7ff8297SAmit Kucheria 			continue;
422a7ff8297SAmit Kucheria 		if (!tsens_threshold_violated(priv, hw_id, &d))
423a7ff8297SAmit Kucheria 			continue;
424a7ff8297SAmit Kucheria 		ret = get_temp_tsens_valid(s, &temp);
425a7ff8297SAmit Kucheria 		if (ret) {
426a7ff8297SAmit Kucheria 			dev_err(priv->dev, "[%u] %s: error reading sensor\n",
427a7ff8297SAmit Kucheria 				hw_id, __func__);
428a7ff8297SAmit Kucheria 			continue;
429a7ff8297SAmit Kucheria 		}
430a7ff8297SAmit Kucheria 
431a7ff8297SAmit Kucheria 		tsens_read_irq_state(priv, hw_id, s, &d);
432a7ff8297SAmit Kucheria 		if (d.crit_viol &&
433a7ff8297SAmit Kucheria 		    !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
434a7ff8297SAmit Kucheria 			/* Mask critical interrupts, unused on Linux */
435a7ff8297SAmit Kucheria 			tsens_set_interrupt(priv, hw_id, CRITICAL, false);
436a7ff8297SAmit Kucheria 		}
437a7ff8297SAmit Kucheria 	}
438a7ff8297SAmit Kucheria 
439a7ff8297SAmit Kucheria 	return IRQ_HANDLED;
440a7ff8297SAmit Kucheria }
441a7ff8297SAmit Kucheria 
442a7ff8297SAmit Kucheria /**
443a7ff8297SAmit Kucheria  * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
444a7ff8297SAmit Kucheria  * @irq: irq number
445a7ff8297SAmit Kucheria  * @data: tsens controller private data
446a7ff8297SAmit Kucheria  *
447a7ff8297SAmit Kucheria  * Check all sensors to find ones that violated their threshold limits. If the
448a7ff8297SAmit Kucheria  * temperature is still outside the limits, call thermal_zone_device_update() to
449a7ff8297SAmit Kucheria  * update the thresholds, else re-enable the interrupts.
450a7ff8297SAmit Kucheria  *
451a7ff8297SAmit Kucheria  * The level-triggered interrupt might deassert if the temperature returned to
452a7ff8297SAmit Kucheria  * within the threshold limits by the time the handler got scheduled. We
453a7ff8297SAmit Kucheria  * consider the irq to have been handled in that case.
454a7ff8297SAmit Kucheria  *
455a7ff8297SAmit Kucheria  * Return: IRQ_HANDLED
456a7ff8297SAmit Kucheria  */
4573ecc8292SAmit Kucheria static irqreturn_t tsens_irq_thread(int irq, void *data)
458a7ff8297SAmit Kucheria {
459a7ff8297SAmit Kucheria 	struct tsens_priv *priv = data;
460a7ff8297SAmit Kucheria 	struct tsens_irq_data d;
461a7ff8297SAmit Kucheria 	bool enable = true, disable = false;
462a7ff8297SAmit Kucheria 	unsigned long flags;
463a7ff8297SAmit Kucheria 	int temp, ret, i;
464a7ff8297SAmit Kucheria 
465a7ff8297SAmit Kucheria 	for (i = 0; i < priv->num_sensors; i++) {
466a7ff8297SAmit Kucheria 		bool trigger = false;
467a7ff8297SAmit Kucheria 		const struct tsens_sensor *s = &priv->sensor[i];
468a7ff8297SAmit Kucheria 		u32 hw_id = s->hw_id;
469a7ff8297SAmit Kucheria 
470*cf969218SAnsuel Smith 		if (!s->tzd)
471a7ff8297SAmit Kucheria 			continue;
472a7ff8297SAmit Kucheria 		if (!tsens_threshold_violated(priv, hw_id, &d))
473a7ff8297SAmit Kucheria 			continue;
474a7ff8297SAmit Kucheria 		ret = get_temp_tsens_valid(s, &temp);
475a7ff8297SAmit Kucheria 		if (ret) {
476a7ff8297SAmit Kucheria 			dev_err(priv->dev, "[%u] %s: error reading sensor\n",
477a7ff8297SAmit Kucheria 				hw_id, __func__);
478a7ff8297SAmit Kucheria 			continue;
479a7ff8297SAmit Kucheria 		}
480a7ff8297SAmit Kucheria 
481a7ff8297SAmit Kucheria 		spin_lock_irqsave(&priv->ul_lock, flags);
482a7ff8297SAmit Kucheria 
483a7ff8297SAmit Kucheria 		tsens_read_irq_state(priv, hw_id, s, &d);
484a7ff8297SAmit Kucheria 
485a7ff8297SAmit Kucheria 		if (d.up_viol &&
486a7ff8297SAmit Kucheria 		    !masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) {
487a7ff8297SAmit Kucheria 			tsens_set_interrupt(priv, hw_id, UPPER, disable);
488a7ff8297SAmit Kucheria 			if (d.up_thresh > temp) {
489a7ff8297SAmit Kucheria 				dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
490a7ff8297SAmit Kucheria 					hw_id, __func__);
491a7ff8297SAmit Kucheria 				tsens_set_interrupt(priv, hw_id, UPPER, enable);
492a7ff8297SAmit Kucheria 			} else {
493a7ff8297SAmit Kucheria 				trigger = true;
494a7ff8297SAmit Kucheria 				/* Keep irq masked */
495a7ff8297SAmit Kucheria 			}
496a7ff8297SAmit Kucheria 		} else if (d.low_viol &&
497a7ff8297SAmit Kucheria 			   !masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) {
498a7ff8297SAmit Kucheria 			tsens_set_interrupt(priv, hw_id, LOWER, disable);
499a7ff8297SAmit Kucheria 			if (d.low_thresh < temp) {
500a7ff8297SAmit Kucheria 				dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
501a7ff8297SAmit Kucheria 					hw_id, __func__);
502a7ff8297SAmit Kucheria 				tsens_set_interrupt(priv, hw_id, LOWER, enable);
503a7ff8297SAmit Kucheria 			} else {
504a7ff8297SAmit Kucheria 				trigger = true;
505a7ff8297SAmit Kucheria 				/* Keep irq masked */
506a7ff8297SAmit Kucheria 			}
507a7ff8297SAmit Kucheria 		}
508a7ff8297SAmit Kucheria 
509a7ff8297SAmit Kucheria 		spin_unlock_irqrestore(&priv->ul_lock, flags);
510a7ff8297SAmit Kucheria 
511a7ff8297SAmit Kucheria 		if (trigger) {
512a7ff8297SAmit Kucheria 			dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
513a7ff8297SAmit Kucheria 				hw_id, __func__, temp);
514a7ff8297SAmit Kucheria 			thermal_zone_device_update(s->tzd,
515a7ff8297SAmit Kucheria 						   THERMAL_EVENT_UNSPECIFIED);
516a7ff8297SAmit Kucheria 		} else {
517a7ff8297SAmit Kucheria 			dev_dbg(priv->dev, "[%u] %s: no violation:  %d\n",
518a7ff8297SAmit Kucheria 				hw_id, __func__, temp);
519a7ff8297SAmit Kucheria 		}
52053e2a20eSAnsuel Smith 
52153e2a20eSAnsuel Smith 		if (tsens_version(priv) < VER_0_1) {
52253e2a20eSAnsuel Smith 			/* Constraint: There is only 1 interrupt control register for all
52353e2a20eSAnsuel Smith 			 * 11 temperature sensor. So monitoring more than 1 sensor based
52453e2a20eSAnsuel Smith 			 * on interrupts will yield inconsistent result. To overcome this
52553e2a20eSAnsuel Smith 			 * issue we will monitor only sensor 0 which is the master sensor.
52653e2a20eSAnsuel Smith 			 */
52753e2a20eSAnsuel Smith 			break;
52853e2a20eSAnsuel Smith 		}
529a7ff8297SAmit Kucheria 	}
530a7ff8297SAmit Kucheria 
531a7ff8297SAmit Kucheria 	return IRQ_HANDLED;
532a7ff8297SAmit Kucheria }
533a7ff8297SAmit Kucheria 
5343ecc8292SAmit Kucheria static int tsens_set_trips(void *_sensor, int low, int high)
535a7ff8297SAmit Kucheria {
536a7ff8297SAmit Kucheria 	struct tsens_sensor *s = _sensor;
537a7ff8297SAmit Kucheria 	struct tsens_priv *priv = s->priv;
538a7ff8297SAmit Kucheria 	struct device *dev = priv->dev;
539a7ff8297SAmit Kucheria 	struct tsens_irq_data d;
540a7ff8297SAmit Kucheria 	unsigned long flags;
541a7ff8297SAmit Kucheria 	int high_val, low_val, cl_high, cl_low;
542a7ff8297SAmit Kucheria 	u32 hw_id = s->hw_id;
543a7ff8297SAmit Kucheria 
54453e2a20eSAnsuel Smith 	if (tsens_version(priv) < VER_0_1) {
54553e2a20eSAnsuel Smith 		/* Pre v0.1 IP had a single register for each type of interrupt
54653e2a20eSAnsuel Smith 		 * and thresholds
54753e2a20eSAnsuel Smith 		 */
54853e2a20eSAnsuel Smith 		hw_id = 0;
54953e2a20eSAnsuel Smith 	}
55053e2a20eSAnsuel Smith 
551a7ff8297SAmit Kucheria 	dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
552a7ff8297SAmit Kucheria 		hw_id, __func__, low, high);
553a7ff8297SAmit Kucheria 
554a7ff8297SAmit Kucheria 	cl_high = clamp_val(high, -40000, 120000);
555a7ff8297SAmit Kucheria 	cl_low  = clamp_val(low, -40000, 120000);
556a7ff8297SAmit Kucheria 
557a7ff8297SAmit Kucheria 	high_val = tsens_mC_to_hw(s, cl_high);
558a7ff8297SAmit Kucheria 	low_val  = tsens_mC_to_hw(s, cl_low);
559a7ff8297SAmit Kucheria 
560a7ff8297SAmit Kucheria 	spin_lock_irqsave(&priv->ul_lock, flags);
561a7ff8297SAmit Kucheria 
562a7ff8297SAmit Kucheria 	tsens_read_irq_state(priv, hw_id, s, &d);
563a7ff8297SAmit Kucheria 
564a7ff8297SAmit Kucheria 	/* Write the new thresholds and clear the status */
565a7ff8297SAmit Kucheria 	regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
566a7ff8297SAmit Kucheria 	regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
567a7ff8297SAmit Kucheria 	tsens_set_interrupt(priv, hw_id, LOWER, true);
568a7ff8297SAmit Kucheria 	tsens_set_interrupt(priv, hw_id, UPPER, true);
569a7ff8297SAmit Kucheria 
570a7ff8297SAmit Kucheria 	spin_unlock_irqrestore(&priv->ul_lock, flags);
571a7ff8297SAmit Kucheria 
572a7ff8297SAmit Kucheria 	dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
573a7ff8297SAmit Kucheria 		hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
574a7ff8297SAmit Kucheria 
575a7ff8297SAmit Kucheria 	return 0;
576a7ff8297SAmit Kucheria }
577a7ff8297SAmit Kucheria 
5783ecc8292SAmit Kucheria static int tsens_enable_irq(struct tsens_priv *priv)
579a7ff8297SAmit Kucheria {
580a7ff8297SAmit Kucheria 	int ret;
581a7ff8297SAmit Kucheria 	int val = tsens_version(priv) > VER_1_X ? 7 : 1;
582a7ff8297SAmit Kucheria 
583a7ff8297SAmit Kucheria 	ret = regmap_field_write(priv->rf[INT_EN], val);
584a7ff8297SAmit Kucheria 	if (ret < 0)
585a7ff8297SAmit Kucheria 		dev_err(priv->dev, "%s: failed to enable interrupts\n",
586a7ff8297SAmit Kucheria 			__func__);
587a7ff8297SAmit Kucheria 
588a7ff8297SAmit Kucheria 	return ret;
589a7ff8297SAmit Kucheria }
590a7ff8297SAmit Kucheria 
5913ecc8292SAmit Kucheria static void tsens_disable_irq(struct tsens_priv *priv)
592a7ff8297SAmit Kucheria {
593a7ff8297SAmit Kucheria 	regmap_field_write(priv->rf[INT_EN], 0);
594a7ff8297SAmit Kucheria }
595a7ff8297SAmit Kucheria 
596a7ff8297SAmit Kucheria int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
597a7ff8297SAmit Kucheria {
598a7ff8297SAmit Kucheria 	struct tsens_priv *priv = s->priv;
599a7ff8297SAmit Kucheria 	int hw_id = s->hw_id;
600a7ff8297SAmit Kucheria 	u32 temp_idx = LAST_TEMP_0 + hw_id;
601a7ff8297SAmit Kucheria 	u32 valid_idx = VALID_0 + hw_id;
602a7ff8297SAmit Kucheria 	u32 valid;
603a7ff8297SAmit Kucheria 	int ret;
604a7ff8297SAmit Kucheria 
60553e2a20eSAnsuel Smith 	/* VER_0 doesn't have VALID bit */
60653e2a20eSAnsuel Smith 	if (tsens_version(priv) >= VER_0_1) {
607a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[valid_idx], &valid);
608a7ff8297SAmit Kucheria 		if (ret)
609a7ff8297SAmit Kucheria 			return ret;
610a7ff8297SAmit Kucheria 		while (!valid) {
611a7ff8297SAmit Kucheria 			/* Valid bit is 0 for 6 AHB clock cycles.
612a7ff8297SAmit Kucheria 			 * At 19.2MHz, 1 AHB clock is ~60ns.
613a7ff8297SAmit Kucheria 			 * We should enter this loop very, very rarely.
614a7ff8297SAmit Kucheria 			 */
615a7ff8297SAmit Kucheria 			ndelay(400);
616a7ff8297SAmit Kucheria 			ret = regmap_field_read(priv->rf[valid_idx], &valid);
617a7ff8297SAmit Kucheria 			if (ret)
618a7ff8297SAmit Kucheria 				return ret;
619a7ff8297SAmit Kucheria 		}
62053e2a20eSAnsuel Smith 	}
621a7ff8297SAmit Kucheria 
622a7ff8297SAmit Kucheria 	/* Valid bit is set, OK to read the temperature */
623a7ff8297SAmit Kucheria 	*temp = tsens_hw_to_mC(s, temp_idx);
624a7ff8297SAmit Kucheria 
625a7ff8297SAmit Kucheria 	return 0;
626a7ff8297SAmit Kucheria }
627a7ff8297SAmit Kucheria 
628a7ff8297SAmit Kucheria int get_temp_common(const struct tsens_sensor *s, int *temp)
629a7ff8297SAmit Kucheria {
630a7ff8297SAmit Kucheria 	struct tsens_priv *priv = s->priv;
631a7ff8297SAmit Kucheria 	int hw_id = s->hw_id;
63253e2a20eSAnsuel Smith 	int last_temp = 0, ret, trdy;
63353e2a20eSAnsuel Smith 	unsigned long timeout;
63453e2a20eSAnsuel Smith 
63553e2a20eSAnsuel Smith 	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
63653e2a20eSAnsuel Smith 	do {
63753e2a20eSAnsuel Smith 		if (tsens_version(priv) == VER_0) {
63853e2a20eSAnsuel Smith 			ret = regmap_field_read(priv->rf[TRDY], &trdy);
63953e2a20eSAnsuel Smith 			if (ret)
64053e2a20eSAnsuel Smith 				return ret;
64153e2a20eSAnsuel Smith 			if (!trdy)
64253e2a20eSAnsuel Smith 				continue;
64353e2a20eSAnsuel Smith 		}
644a7ff8297SAmit Kucheria 
645a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
646a7ff8297SAmit Kucheria 		if (ret)
647a7ff8297SAmit Kucheria 			return ret;
648a7ff8297SAmit Kucheria 
649a7ff8297SAmit Kucheria 		*temp = code_to_degc(last_temp, s) * 1000;
650a7ff8297SAmit Kucheria 
651a7ff8297SAmit Kucheria 		return 0;
65253e2a20eSAnsuel Smith 	} while (time_before(jiffies, timeout));
65353e2a20eSAnsuel Smith 
65453e2a20eSAnsuel Smith 	return -ETIMEDOUT;
655a7ff8297SAmit Kucheria }
656a7ff8297SAmit Kucheria 
657a7ff8297SAmit Kucheria #ifdef CONFIG_DEBUG_FS
658a7ff8297SAmit Kucheria static int dbg_sensors_show(struct seq_file *s, void *data)
659a7ff8297SAmit Kucheria {
660a7ff8297SAmit Kucheria 	struct platform_device *pdev = s->private;
661a7ff8297SAmit Kucheria 	struct tsens_priv *priv = platform_get_drvdata(pdev);
662a7ff8297SAmit Kucheria 	int i;
663a7ff8297SAmit Kucheria 
664a7ff8297SAmit Kucheria 	seq_printf(s, "max: %2d\nnum: %2d\n\n",
665a7ff8297SAmit Kucheria 		   priv->feat->max_sensors, priv->num_sensors);
666a7ff8297SAmit Kucheria 
667a7ff8297SAmit Kucheria 	seq_puts(s, "      id    slope   offset\n--------------------------\n");
668a7ff8297SAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
669a7ff8297SAmit Kucheria 		seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id,
670a7ff8297SAmit Kucheria 			   priv->sensor[i].slope, priv->sensor[i].offset);
671a7ff8297SAmit Kucheria 	}
672a7ff8297SAmit Kucheria 
673a7ff8297SAmit Kucheria 	return 0;
674a7ff8297SAmit Kucheria }
675a7ff8297SAmit Kucheria 
676a7ff8297SAmit Kucheria static int dbg_version_show(struct seq_file *s, void *data)
677a7ff8297SAmit Kucheria {
678a7ff8297SAmit Kucheria 	struct platform_device *pdev = s->private;
679a7ff8297SAmit Kucheria 	struct tsens_priv *priv = platform_get_drvdata(pdev);
680a7ff8297SAmit Kucheria 	u32 maj_ver, min_ver, step_ver;
681a7ff8297SAmit Kucheria 	int ret;
682a7ff8297SAmit Kucheria 
683a7ff8297SAmit Kucheria 	if (tsens_version(priv) > VER_0_1) {
684a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver);
685a7ff8297SAmit Kucheria 		if (ret)
686a7ff8297SAmit Kucheria 			return ret;
687a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver);
688a7ff8297SAmit Kucheria 		if (ret)
689a7ff8297SAmit Kucheria 			return ret;
690a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[VER_STEP], &step_ver);
691a7ff8297SAmit Kucheria 		if (ret)
692a7ff8297SAmit Kucheria 			return ret;
693a7ff8297SAmit Kucheria 		seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
694a7ff8297SAmit Kucheria 	} else {
695a7ff8297SAmit Kucheria 		seq_puts(s, "0.1.0\n");
696a7ff8297SAmit Kucheria 	}
697a7ff8297SAmit Kucheria 
698a7ff8297SAmit Kucheria 	return 0;
699a7ff8297SAmit Kucheria }
700a7ff8297SAmit Kucheria 
701a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_version);
702a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_sensors);
703a7ff8297SAmit Kucheria 
704a7ff8297SAmit Kucheria static void tsens_debug_init(struct platform_device *pdev)
705a7ff8297SAmit Kucheria {
706a7ff8297SAmit Kucheria 	struct tsens_priv *priv = platform_get_drvdata(pdev);
707a7ff8297SAmit Kucheria 	struct dentry *root, *file;
708a7ff8297SAmit Kucheria 
709a7ff8297SAmit Kucheria 	root = debugfs_lookup("tsens", NULL);
710a7ff8297SAmit Kucheria 	if (!root)
711a7ff8297SAmit Kucheria 		priv->debug_root = debugfs_create_dir("tsens", NULL);
712a7ff8297SAmit Kucheria 	else
713a7ff8297SAmit Kucheria 		priv->debug_root = root;
714a7ff8297SAmit Kucheria 
715a7ff8297SAmit Kucheria 	file = debugfs_lookup("version", priv->debug_root);
716a7ff8297SAmit Kucheria 	if (!file)
717a7ff8297SAmit Kucheria 		debugfs_create_file("version", 0444, priv->debug_root,
718a7ff8297SAmit Kucheria 				    pdev, &dbg_version_fops);
719a7ff8297SAmit Kucheria 
720a7ff8297SAmit Kucheria 	/* A directory for each instance of the TSENS IP */
721a7ff8297SAmit Kucheria 	priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
722a7ff8297SAmit Kucheria 	debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
723a7ff8297SAmit Kucheria }
724a7ff8297SAmit Kucheria #else
725a7ff8297SAmit Kucheria static inline void tsens_debug_init(struct platform_device *pdev) {}
726a7ff8297SAmit Kucheria #endif
727a7ff8297SAmit Kucheria 
728a7ff8297SAmit Kucheria static const struct regmap_config tsens_config = {
729a7ff8297SAmit Kucheria 	.name		= "tm",
730a7ff8297SAmit Kucheria 	.reg_bits	= 32,
731a7ff8297SAmit Kucheria 	.val_bits	= 32,
732a7ff8297SAmit Kucheria 	.reg_stride	= 4,
733a7ff8297SAmit Kucheria };
734a7ff8297SAmit Kucheria 
735a7ff8297SAmit Kucheria static const struct regmap_config tsens_srot_config = {
736a7ff8297SAmit Kucheria 	.name		= "srot",
737a7ff8297SAmit Kucheria 	.reg_bits	= 32,
738a7ff8297SAmit Kucheria 	.val_bits	= 32,
739a7ff8297SAmit Kucheria 	.reg_stride	= 4,
740a7ff8297SAmit Kucheria };
741a7ff8297SAmit Kucheria 
742a7ff8297SAmit Kucheria int __init init_common(struct tsens_priv *priv)
743a7ff8297SAmit Kucheria {
744a7ff8297SAmit Kucheria 	void __iomem *tm_base, *srot_base;
745a7ff8297SAmit Kucheria 	struct device *dev = priv->dev;
746a7ff8297SAmit Kucheria 	u32 ver_minor;
747a7ff8297SAmit Kucheria 	struct resource *res;
748a7ff8297SAmit Kucheria 	u32 enabled;
749a7ff8297SAmit Kucheria 	int ret, i, j;
750a7ff8297SAmit Kucheria 	struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
751a7ff8297SAmit Kucheria 
752a7ff8297SAmit Kucheria 	if (!op)
753a7ff8297SAmit Kucheria 		return -EINVAL;
754a7ff8297SAmit Kucheria 
755a7ff8297SAmit Kucheria 	if (op->num_resources > 1) {
756a7ff8297SAmit Kucheria 		/* DT with separate SROT and TM address space */
757a7ff8297SAmit Kucheria 		priv->tm_offset = 0;
758a7ff8297SAmit Kucheria 		res = platform_get_resource(op, IORESOURCE_MEM, 1);
759a7ff8297SAmit Kucheria 		srot_base = devm_ioremap_resource(dev, res);
760a7ff8297SAmit Kucheria 		if (IS_ERR(srot_base)) {
761a7ff8297SAmit Kucheria 			ret = PTR_ERR(srot_base);
762a7ff8297SAmit Kucheria 			goto err_put_device;
763a7ff8297SAmit Kucheria 		}
764a7ff8297SAmit Kucheria 
765a7ff8297SAmit Kucheria 		priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
766a7ff8297SAmit Kucheria 						       &tsens_srot_config);
767a7ff8297SAmit Kucheria 		if (IS_ERR(priv->srot_map)) {
768a7ff8297SAmit Kucheria 			ret = PTR_ERR(priv->srot_map);
769a7ff8297SAmit Kucheria 			goto err_put_device;
770a7ff8297SAmit Kucheria 		}
771a7ff8297SAmit Kucheria 	} else {
772a7ff8297SAmit Kucheria 		/* old DTs where SROT and TM were in a contiguous 2K block */
773a7ff8297SAmit Kucheria 		priv->tm_offset = 0x1000;
774a7ff8297SAmit Kucheria 	}
775a7ff8297SAmit Kucheria 
77653e2a20eSAnsuel Smith 	if (tsens_version(priv) >= VER_0_1) {
777a7ff8297SAmit Kucheria 		res = platform_get_resource(op, IORESOURCE_MEM, 0);
778a7ff8297SAmit Kucheria 		tm_base = devm_ioremap_resource(dev, res);
779a7ff8297SAmit Kucheria 		if (IS_ERR(tm_base)) {
780a7ff8297SAmit Kucheria 			ret = PTR_ERR(tm_base);
781a7ff8297SAmit Kucheria 			goto err_put_device;
782a7ff8297SAmit Kucheria 		}
783a7ff8297SAmit Kucheria 
784a7ff8297SAmit Kucheria 		priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
78553e2a20eSAnsuel Smith 	} else { /* VER_0 share the same gcc regs using a syscon */
78653e2a20eSAnsuel Smith 		struct device *parent = priv->dev->parent;
78753e2a20eSAnsuel Smith 
78853e2a20eSAnsuel Smith 		if (parent)
78953e2a20eSAnsuel Smith 			priv->tm_map = syscon_node_to_regmap(parent->of_node);
79053e2a20eSAnsuel Smith 	}
79153e2a20eSAnsuel Smith 
79253e2a20eSAnsuel Smith 	if (IS_ERR_OR_NULL(priv->tm_map)) {
79353e2a20eSAnsuel Smith 		if (!priv->tm_map)
79453e2a20eSAnsuel Smith 			ret = -ENODEV;
79553e2a20eSAnsuel Smith 		else
796a7ff8297SAmit Kucheria 			ret = PTR_ERR(priv->tm_map);
797a7ff8297SAmit Kucheria 		goto err_put_device;
798a7ff8297SAmit Kucheria 	}
799a7ff8297SAmit Kucheria 
80053e2a20eSAnsuel Smith 	/* VER_0 have only tm_map */
80153e2a20eSAnsuel Smith 	if (!priv->srot_map)
80253e2a20eSAnsuel Smith 		priv->srot_map = priv->tm_map;
80353e2a20eSAnsuel Smith 
804a7ff8297SAmit Kucheria 	if (tsens_version(priv) > VER_0_1) {
805a7ff8297SAmit Kucheria 		for (i = VER_MAJOR; i <= VER_STEP; i++) {
806a7ff8297SAmit Kucheria 			priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
807a7ff8297SAmit Kucheria 							      priv->fields[i]);
808f4136863SGuangqing Zhu 			if (IS_ERR(priv->rf[i])) {
809f4136863SGuangqing Zhu 				ret = PTR_ERR(priv->rf[i]);
810f4136863SGuangqing Zhu 				goto err_put_device;
811f4136863SGuangqing Zhu 			}
812a7ff8297SAmit Kucheria 		}
813a7ff8297SAmit Kucheria 		ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
814a7ff8297SAmit Kucheria 		if (ret)
815a7ff8297SAmit Kucheria 			goto err_put_device;
816a7ff8297SAmit Kucheria 	}
817a7ff8297SAmit Kucheria 
818a7ff8297SAmit Kucheria 	priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
819a7ff8297SAmit Kucheria 						     priv->fields[TSENS_EN]);
820a7ff8297SAmit Kucheria 	if (IS_ERR(priv->rf[TSENS_EN])) {
821a7ff8297SAmit Kucheria 		ret = PTR_ERR(priv->rf[TSENS_EN]);
822a7ff8297SAmit Kucheria 		goto err_put_device;
823a7ff8297SAmit Kucheria 	}
82453e2a20eSAnsuel Smith 	/* in VER_0 TSENS need to be explicitly enabled */
82553e2a20eSAnsuel Smith 	if (tsens_version(priv) == VER_0)
82653e2a20eSAnsuel Smith 		regmap_field_write(priv->rf[TSENS_EN], 1);
82753e2a20eSAnsuel Smith 
828a7ff8297SAmit Kucheria 	ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
829a7ff8297SAmit Kucheria 	if (ret)
830a7ff8297SAmit Kucheria 		goto err_put_device;
831a7ff8297SAmit Kucheria 	if (!enabled) {
832a7ff8297SAmit Kucheria 		dev_err(dev, "%s: device not enabled\n", __func__);
833a7ff8297SAmit Kucheria 		ret = -ENODEV;
834a7ff8297SAmit Kucheria 		goto err_put_device;
835a7ff8297SAmit Kucheria 	}
836a7ff8297SAmit Kucheria 
837a7ff8297SAmit Kucheria 	priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
838a7ff8297SAmit Kucheria 						      priv->fields[SENSOR_EN]);
839a7ff8297SAmit Kucheria 	if (IS_ERR(priv->rf[SENSOR_EN])) {
840a7ff8297SAmit Kucheria 		ret = PTR_ERR(priv->rf[SENSOR_EN]);
841a7ff8297SAmit Kucheria 		goto err_put_device;
842a7ff8297SAmit Kucheria 	}
843a7ff8297SAmit Kucheria 	priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
844a7ff8297SAmit Kucheria 						   priv->fields[INT_EN]);
845a7ff8297SAmit Kucheria 	if (IS_ERR(priv->rf[INT_EN])) {
846a7ff8297SAmit Kucheria 		ret = PTR_ERR(priv->rf[INT_EN]);
847a7ff8297SAmit Kucheria 		goto err_put_device;
848a7ff8297SAmit Kucheria 	}
849a7ff8297SAmit Kucheria 
85053e2a20eSAnsuel Smith 	priv->rf[TSENS_SW_RST] =
85153e2a20eSAnsuel Smith 		devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]);
85253e2a20eSAnsuel Smith 	if (IS_ERR(priv->rf[TSENS_SW_RST])) {
85353e2a20eSAnsuel Smith 		ret = PTR_ERR(priv->rf[TSENS_SW_RST]);
85453e2a20eSAnsuel Smith 		goto err_put_device;
85553e2a20eSAnsuel Smith 	}
85653e2a20eSAnsuel Smith 
85753e2a20eSAnsuel Smith 	priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]);
85853e2a20eSAnsuel Smith 	if (IS_ERR(priv->rf[TRDY])) {
85953e2a20eSAnsuel Smith 		ret = PTR_ERR(priv->rf[TRDY]);
86053e2a20eSAnsuel Smith 		goto err_put_device;
86153e2a20eSAnsuel Smith 	}
86253e2a20eSAnsuel Smith 
863a7ff8297SAmit Kucheria 	/* This loop might need changes if enum regfield_ids is reordered */
864a7ff8297SAmit Kucheria 	for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
865a7ff8297SAmit Kucheria 		for (i = 0; i < priv->feat->max_sensors; i++) {
866a7ff8297SAmit Kucheria 			int idx = j + i;
867a7ff8297SAmit Kucheria 
868a7ff8297SAmit Kucheria 			priv->rf[idx] = devm_regmap_field_alloc(dev,
869a7ff8297SAmit Kucheria 								priv->tm_map,
870a7ff8297SAmit Kucheria 								priv->fields[idx]);
871a7ff8297SAmit Kucheria 			if (IS_ERR(priv->rf[idx])) {
872a7ff8297SAmit Kucheria 				ret = PTR_ERR(priv->rf[idx]);
873a7ff8297SAmit Kucheria 				goto err_put_device;
874a7ff8297SAmit Kucheria 			}
875a7ff8297SAmit Kucheria 		}
876a7ff8297SAmit Kucheria 	}
877a7ff8297SAmit Kucheria 
87853e2a20eSAnsuel Smith 	if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) {
879a7ff8297SAmit Kucheria 		/* Loop might need changes if enum regfield_ids is reordered */
880a7ff8297SAmit Kucheria 		for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
881a7ff8297SAmit Kucheria 			for (i = 0; i < priv->feat->max_sensors; i++) {
882a7ff8297SAmit Kucheria 				int idx = j + i;
883a7ff8297SAmit Kucheria 
884a7ff8297SAmit Kucheria 				priv->rf[idx] =
885a7ff8297SAmit Kucheria 					devm_regmap_field_alloc(dev,
886a7ff8297SAmit Kucheria 								priv->tm_map,
887a7ff8297SAmit Kucheria 								priv->fields[idx]);
888a7ff8297SAmit Kucheria 				if (IS_ERR(priv->rf[idx])) {
889a7ff8297SAmit Kucheria 					ret = PTR_ERR(priv->rf[idx]);
890a7ff8297SAmit Kucheria 					goto err_put_device;
891a7ff8297SAmit Kucheria 				}
892a7ff8297SAmit Kucheria 			}
893a7ff8297SAmit Kucheria 		}
894a7ff8297SAmit Kucheria 	}
895a7ff8297SAmit Kucheria 
896a7ff8297SAmit Kucheria 	if (tsens_version(priv) > VER_1_X &&  ver_minor > 2) {
897a7ff8297SAmit Kucheria 		/* Watchdog is present only on v2.3+ */
898a7ff8297SAmit Kucheria 		priv->feat->has_watchdog = 1;
899a7ff8297SAmit Kucheria 		for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
900a7ff8297SAmit Kucheria 			priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
901a7ff8297SAmit Kucheria 							      priv->fields[i]);
902a7ff8297SAmit Kucheria 			if (IS_ERR(priv->rf[i])) {
903a7ff8297SAmit Kucheria 				ret = PTR_ERR(priv->rf[i]);
904a7ff8297SAmit Kucheria 				goto err_put_device;
905a7ff8297SAmit Kucheria 			}
906a7ff8297SAmit Kucheria 		}
907a7ff8297SAmit Kucheria 		/*
908a7ff8297SAmit Kucheria 		 * Watchdog is already enabled, unmask the bark.
909a7ff8297SAmit Kucheria 		 * Disable cycle completion monitoring
910a7ff8297SAmit Kucheria 		 */
911a7ff8297SAmit Kucheria 		regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
912a7ff8297SAmit Kucheria 		regmap_field_write(priv->rf[CC_MON_MASK], 1);
913a7ff8297SAmit Kucheria 	}
914a7ff8297SAmit Kucheria 
915a7ff8297SAmit Kucheria 	spin_lock_init(&priv->ul_lock);
91653e2a20eSAnsuel Smith 
91753e2a20eSAnsuel Smith 	/* VER_0 interrupt doesn't need to be enabled */
91853e2a20eSAnsuel Smith 	if (tsens_version(priv) >= VER_0_1)
919a7ff8297SAmit Kucheria 		tsens_enable_irq(priv);
92053e2a20eSAnsuel Smith 
921a7ff8297SAmit Kucheria 	tsens_debug_init(op);
922a7ff8297SAmit Kucheria 
923a7ff8297SAmit Kucheria err_put_device:
924a7ff8297SAmit Kucheria 	put_device(&op->dev);
925a7ff8297SAmit Kucheria 	return ret;
926a7ff8297SAmit Kucheria }
927a7ff8297SAmit Kucheria 
9289066073cSRajendra Nayak static int tsens_get_temp(void *data, int *temp)
9299066073cSRajendra Nayak {
9308b71bce4SAmit Kucheria 	struct tsens_sensor *s = data;
93169b628acSAmit Kucheria 	struct tsens_priv *priv = s->priv;
9329066073cSRajendra Nayak 
9338b71bce4SAmit Kucheria 	return priv->ops->get_temp(s, temp);
9349066073cSRajendra Nayak }
9359066073cSRajendra Nayak 
9362cbcd2eaSAmit Kucheria static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
9379066073cSRajendra Nayak {
9388b71bce4SAmit Kucheria 	struct tsens_sensor *s = data;
93969b628acSAmit Kucheria 	struct tsens_priv *priv = s->priv;
9409066073cSRajendra Nayak 
94169b628acSAmit Kucheria 	if (priv->ops->get_trend)
9428b71bce4SAmit Kucheria 		return priv->ops->get_trend(s, trend);
9439066073cSRajendra Nayak 
9449066073cSRajendra Nayak 	return -ENOTSUPP;
9459066073cSRajendra Nayak }
9469066073cSRajendra Nayak 
9475b97469aSArnd Bergmann static int  __maybe_unused tsens_suspend(struct device *dev)
9489066073cSRajendra Nayak {
94969b628acSAmit Kucheria 	struct tsens_priv *priv = dev_get_drvdata(dev);
9509066073cSRajendra Nayak 
95169b628acSAmit Kucheria 	if (priv->ops && priv->ops->suspend)
95269b628acSAmit Kucheria 		return priv->ops->suspend(priv);
9539066073cSRajendra Nayak 
9549066073cSRajendra Nayak 	return 0;
9559066073cSRajendra Nayak }
9569066073cSRajendra Nayak 
9575b97469aSArnd Bergmann static int __maybe_unused tsens_resume(struct device *dev)
9589066073cSRajendra Nayak {
95969b628acSAmit Kucheria 	struct tsens_priv *priv = dev_get_drvdata(dev);
9609066073cSRajendra Nayak 
96169b628acSAmit Kucheria 	if (priv->ops && priv->ops->resume)
96269b628acSAmit Kucheria 		return priv->ops->resume(priv);
9639066073cSRajendra Nayak 
9649066073cSRajendra Nayak 	return 0;
9659066073cSRajendra Nayak }
9669066073cSRajendra Nayak 
9679066073cSRajendra Nayak static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
9689066073cSRajendra Nayak 
9699066073cSRajendra Nayak static const struct of_device_id tsens_table[] = {
9709066073cSRajendra Nayak 	{
9716b3aeafbSAnsuel Smith 		.compatible = "qcom,ipq8064-tsens",
9726b3aeafbSAnsuel Smith 		.data = &data_8960,
9736b3aeafbSAnsuel Smith 	}, {
974a2149ab8SKonrad Dybcio 		.compatible = "qcom,mdm9607-tsens",
975a2149ab8SKonrad Dybcio 		.data = &data_9607,
976a2149ab8SKonrad Dybcio 	}, {
9779066073cSRajendra Nayak 		.compatible = "qcom,msm8916-tsens",
978840a5bd3SRajendra Nayak 		.data = &data_8916,
9799066073cSRajendra Nayak 	}, {
980332bc8ebSShawn Guo 		.compatible = "qcom,msm8939-tsens",
981332bc8ebSShawn Guo 		.data = &data_8939,
982332bc8ebSShawn Guo 	}, {
9839066073cSRajendra Nayak 		.compatible = "qcom,msm8974-tsens",
9845e6703bdSRajendra Nayak 		.data = &data_8974,
985d059c739SRajendra Nayak 	}, {
9860e580290SAngeloGioacchino Del Regno 		.compatible = "qcom,msm8976-tsens",
9870e580290SAngeloGioacchino Del Regno 		.data = &data_8976,
9880e580290SAngeloGioacchino Del Regno 	}, {
989d059c739SRajendra Nayak 		.compatible = "qcom,msm8996-tsens",
990d059c739SRajendra Nayak 		.data = &data_8996,
991191dc74bSAmit Kucheria 	}, {
992e8c24c6fSAmit Kucheria 		.compatible = "qcom,tsens-v1",
993e8c24c6fSAmit Kucheria 		.data = &data_tsens_v1,
994e8c24c6fSAmit Kucheria 	}, {
995191dc74bSAmit Kucheria 		.compatible = "qcom,tsens-v2",
996191dc74bSAmit Kucheria 		.data = &data_tsens_v2,
9979066073cSRajendra Nayak 	},
9989066073cSRajendra Nayak 	{}
9999066073cSRajendra Nayak };
10009066073cSRajendra Nayak MODULE_DEVICE_TABLE(of, tsens_table);
10019066073cSRajendra Nayak 
10029066073cSRajendra Nayak static const struct thermal_zone_of_device_ops tsens_of_ops = {
10039066073cSRajendra Nayak 	.get_temp = tsens_get_temp,
10049066073cSRajendra Nayak 	.get_trend = tsens_get_trend,
1005634e11d5SAmit Kucheria 	.set_trips = tsens_set_trips,
10069066073cSRajendra Nayak };
10079066073cSRajendra Nayak 
100879125e03SAmit Kucheria static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
100979125e03SAmit Kucheria 			      irq_handler_t thread_fn)
101079125e03SAmit Kucheria {
101179125e03SAmit Kucheria 	struct platform_device *pdev;
101279125e03SAmit Kucheria 	int ret, irq;
101379125e03SAmit Kucheria 
101479125e03SAmit Kucheria 	pdev = of_find_device_by_node(priv->dev->of_node);
101579125e03SAmit Kucheria 	if (!pdev)
101679125e03SAmit Kucheria 		return -ENODEV;
101779125e03SAmit Kucheria 
101879125e03SAmit Kucheria 	irq = platform_get_irq_byname(pdev, irqname);
101979125e03SAmit Kucheria 	if (irq < 0) {
102079125e03SAmit Kucheria 		ret = irq;
102179125e03SAmit Kucheria 		/* For old DTs with no IRQ defined */
102279125e03SAmit Kucheria 		if (irq == -ENXIO)
102379125e03SAmit Kucheria 			ret = 0;
102479125e03SAmit Kucheria 	} else {
102553e2a20eSAnsuel Smith 		/* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */
102653e2a20eSAnsuel Smith 		if (tsens_version(priv) == VER_0)
102779125e03SAmit Kucheria 			ret = devm_request_threaded_irq(&pdev->dev, irq,
102853e2a20eSAnsuel Smith 							thread_fn, NULL,
102953e2a20eSAnsuel Smith 							IRQF_TRIGGER_RISING,
103053e2a20eSAnsuel Smith 							dev_name(&pdev->dev),
103153e2a20eSAnsuel Smith 							priv);
103253e2a20eSAnsuel Smith 		else
103353e2a20eSAnsuel Smith 			ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
103453e2a20eSAnsuel Smith 							thread_fn, IRQF_ONESHOT,
103553e2a20eSAnsuel Smith 							dev_name(&pdev->dev),
103653e2a20eSAnsuel Smith 							priv);
103753e2a20eSAnsuel Smith 
103879125e03SAmit Kucheria 		if (ret)
103979125e03SAmit Kucheria 			dev_err(&pdev->dev, "%s: failed to get irq\n",
104079125e03SAmit Kucheria 				__func__);
104179125e03SAmit Kucheria 		else
104279125e03SAmit Kucheria 			enable_irq_wake(irq);
104379125e03SAmit Kucheria 	}
104479125e03SAmit Kucheria 
104579125e03SAmit Kucheria 	put_device(&pdev->dev);
104679125e03SAmit Kucheria 	return ret;
104779125e03SAmit Kucheria }
104879125e03SAmit Kucheria 
104969b628acSAmit Kucheria static int tsens_register(struct tsens_priv *priv)
10509066073cSRajendra Nayak {
105179125e03SAmit Kucheria 	int i, ret;
10529066073cSRajendra Nayak 	struct thermal_zone_device *tzd;
10539066073cSRajendra Nayak 
105469b628acSAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
105569b628acSAmit Kucheria 		priv->sensor[i].priv = priv;
10568b71bce4SAmit Kucheria 		tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id,
105769b628acSAmit Kucheria 							   &priv->sensor[i],
10589066073cSRajendra Nayak 							   &tsens_of_ops);
10599066073cSRajendra Nayak 		if (IS_ERR(tzd))
10609066073cSRajendra Nayak 			continue;
106169b628acSAmit Kucheria 		priv->sensor[i].tzd = tzd;
106269b628acSAmit Kucheria 		if (priv->ops->enable)
106369b628acSAmit Kucheria 			priv->ops->enable(priv, i);
10649066073cSRajendra Nayak 	}
1065634e11d5SAmit Kucheria 
106653e2a20eSAnsuel Smith 	/* VER_0 require to set MIN and MAX THRESH
106753e2a20eSAnsuel Smith 	 * These 2 regs are set using the:
106853e2a20eSAnsuel Smith 	 * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C
106953e2a20eSAnsuel Smith 	 * - CRIT_THRESH_1 for MIN THRESH hardcoded to   0°C
107053e2a20eSAnsuel Smith 	 */
107153e2a20eSAnsuel Smith 	if (tsens_version(priv) < VER_0_1) {
107253e2a20eSAnsuel Smith 		regmap_field_write(priv->rf[CRIT_THRESH_0],
107353e2a20eSAnsuel Smith 				   tsens_mC_to_hw(priv->sensor, 120000));
107453e2a20eSAnsuel Smith 
107553e2a20eSAnsuel Smith 		regmap_field_write(priv->rf[CRIT_THRESH_1],
107653e2a20eSAnsuel Smith 				   tsens_mC_to_hw(priv->sensor, 0));
107753e2a20eSAnsuel Smith 	}
107853e2a20eSAnsuel Smith 
107979125e03SAmit Kucheria 	ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
108079125e03SAmit Kucheria 	if (ret < 0)
108179125e03SAmit Kucheria 		return ret;
1082634e11d5SAmit Kucheria 
108379125e03SAmit Kucheria 	if (priv->feat->crit_int)
108479125e03SAmit Kucheria 		ret = tsens_register_irq(priv, "critical",
108579125e03SAmit Kucheria 					 tsens_critical_irq_thread);
1086634e11d5SAmit Kucheria 
1087634e11d5SAmit Kucheria 	return ret;
10889066073cSRajendra Nayak }
10899066073cSRajendra Nayak 
10909066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev)
10919066073cSRajendra Nayak {
10929066073cSRajendra Nayak 	int ret, i;
10939066073cSRajendra Nayak 	struct device *dev;
10949066073cSRajendra Nayak 	struct device_node *np;
109569b628acSAmit Kucheria 	struct tsens_priv *priv;
10963c040ce0SAmit Kucheria 	const struct tsens_plat_data *data;
10979066073cSRajendra Nayak 	const struct of_device_id *id;
10986d7c70d1SBjorn Andersson 	u32 num_sensors;
10999066073cSRajendra Nayak 
11009066073cSRajendra Nayak 	if (pdev->dev.of_node)
11019066073cSRajendra Nayak 		dev = &pdev->dev;
11029066073cSRajendra Nayak 	else
11039066073cSRajendra Nayak 		dev = pdev->dev.parent;
11049066073cSRajendra Nayak 
11059066073cSRajendra Nayak 	np = dev->of_node;
11069066073cSRajendra Nayak 
11079066073cSRajendra Nayak 	id = of_match_node(tsens_table, np);
110820d4fd84SRajendra Nayak 	if (id)
11099066073cSRajendra Nayak 		data = id->data;
111020d4fd84SRajendra Nayak 	else
111120d4fd84SRajendra Nayak 		data = &data_8960;
11129066073cSRajendra Nayak 
11136d7c70d1SBjorn Andersson 	num_sensors = data->num_sensors;
11146d7c70d1SBjorn Andersson 
11156d7c70d1SBjorn Andersson 	if (np)
11166d7c70d1SBjorn Andersson 		of_property_read_u32(np, "#qcom,sensors", &num_sensors);
11176d7c70d1SBjorn Andersson 
11186d7c70d1SBjorn Andersson 	if (num_sensors <= 0) {
11193795ad5eSAmit Kucheria 		dev_err(dev, "%s: invalid number of sensors\n", __func__);
11209066073cSRajendra Nayak 		return -EINVAL;
11219066073cSRajendra Nayak 	}
11229066073cSRajendra Nayak 
112369b628acSAmit Kucheria 	priv = devm_kzalloc(dev,
112469b628acSAmit Kucheria 			     struct_size(priv, sensor, num_sensors),
11250ed2dd03SKees Cook 			     GFP_KERNEL);
112669b628acSAmit Kucheria 	if (!priv)
11279066073cSRajendra Nayak 		return -ENOMEM;
11289066073cSRajendra Nayak 
112969b628acSAmit Kucheria 	priv->dev = dev;
113069b628acSAmit Kucheria 	priv->num_sensors = num_sensors;
113169b628acSAmit Kucheria 	priv->ops = data->ops;
113269b628acSAmit Kucheria 	for (i = 0;  i < priv->num_sensors; i++) {
11339066073cSRajendra Nayak 		if (data->hw_ids)
113469b628acSAmit Kucheria 			priv->sensor[i].hw_id = data->hw_ids[i];
11359066073cSRajendra Nayak 		else
113669b628acSAmit Kucheria 			priv->sensor[i].hw_id = i;
11379066073cSRajendra Nayak 	}
1138c1997054SAmit Kucheria 	priv->feat = data->feat;
1139c1997054SAmit Kucheria 	priv->fields = data->fields;
11409066073cSRajendra Nayak 
11410e9c0bc7SAmit Kucheria 	platform_set_drvdata(pdev, priv);
11420e9c0bc7SAmit Kucheria 
114369b628acSAmit Kucheria 	if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
11449066073cSRajendra Nayak 		return -EINVAL;
11459066073cSRajendra Nayak 
114669b628acSAmit Kucheria 	ret = priv->ops->init(priv);
11479066073cSRajendra Nayak 	if (ret < 0) {
11483795ad5eSAmit Kucheria 		dev_err(dev, "%s: init failed\n", __func__);
11499066073cSRajendra Nayak 		return ret;
11509066073cSRajendra Nayak 	}
11519066073cSRajendra Nayak 
115269b628acSAmit Kucheria 	if (priv->ops->calibrate) {
115369b628acSAmit Kucheria 		ret = priv->ops->calibrate(priv);
11549066073cSRajendra Nayak 		if (ret < 0) {
1155fc7d18cfSAmit Kucheria 			if (ret != -EPROBE_DEFER)
11563795ad5eSAmit Kucheria 				dev_err(dev, "%s: calibration failed\n", __func__);
11579066073cSRajendra Nayak 			return ret;
11589066073cSRajendra Nayak 		}
11599066073cSRajendra Nayak 	}
11609066073cSRajendra Nayak 
11610e9c0bc7SAmit Kucheria 	return tsens_register(priv);
11629066073cSRajendra Nayak }
11639066073cSRajendra Nayak 
11649066073cSRajendra Nayak static int tsens_remove(struct platform_device *pdev)
11659066073cSRajendra Nayak {
116669b628acSAmit Kucheria 	struct tsens_priv *priv = platform_get_drvdata(pdev);
11679066073cSRajendra Nayak 
11687c938f48SAmit Kucheria 	debugfs_remove_recursive(priv->debug_root);
1169634e11d5SAmit Kucheria 	tsens_disable_irq(priv);
117069b628acSAmit Kucheria 	if (priv->ops->disable)
117169b628acSAmit Kucheria 		priv->ops->disable(priv);
11729066073cSRajendra Nayak 
11739066073cSRajendra Nayak 	return 0;
11749066073cSRajendra Nayak }
11759066073cSRajendra Nayak 
11769066073cSRajendra Nayak static struct platform_driver tsens_driver = {
11779066073cSRajendra Nayak 	.probe = tsens_probe,
11789066073cSRajendra Nayak 	.remove = tsens_remove,
11799066073cSRajendra Nayak 	.driver = {
11809066073cSRajendra Nayak 		.name = "qcom-tsens",
11819066073cSRajendra Nayak 		.pm	= &tsens_pm_ops,
11829066073cSRajendra Nayak 		.of_match_table = tsens_table,
11839066073cSRajendra Nayak 	},
11849066073cSRajendra Nayak };
11859066073cSRajendra Nayak module_platform_driver(tsens_driver);
11869066073cSRajendra Nayak 
11879066073cSRajendra Nayak MODULE_LICENSE("GPL v2");
11889066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
11899066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens");
1190