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>
218556e19dSDmitry Baryshkov #include "../thermal_hwmon.h"
229066073cSRajendra Nayak #include "tsens.h"
239066073cSRajendra Nayak
24a7ff8297SAmit Kucheria /**
25a7ff8297SAmit Kucheria * struct tsens_irq_data - IRQ status and temperature violations
26a7ff8297SAmit Kucheria * @up_viol: upper threshold violated
27a7ff8297SAmit Kucheria * @up_thresh: upper threshold temperature value
28a7ff8297SAmit Kucheria * @up_irq_mask: mask register for upper threshold irqs
29a7ff8297SAmit Kucheria * @up_irq_clear: clear register for uppper threshold irqs
30a7ff8297SAmit Kucheria * @low_viol: lower threshold violated
31a7ff8297SAmit Kucheria * @low_thresh: lower threshold temperature value
32a7ff8297SAmit Kucheria * @low_irq_mask: mask register for lower threshold irqs
33a7ff8297SAmit Kucheria * @low_irq_clear: clear register for lower threshold irqs
34a7ff8297SAmit Kucheria * @crit_viol: critical threshold violated
35a7ff8297SAmit Kucheria * @crit_thresh: critical threshold temperature value
36a7ff8297SAmit Kucheria * @crit_irq_mask: mask register for critical threshold irqs
37a7ff8297SAmit Kucheria * @crit_irq_clear: clear register for critical threshold irqs
38a7ff8297SAmit Kucheria *
39a7ff8297SAmit Kucheria * Structure containing data about temperature threshold settings and
40a7ff8297SAmit Kucheria * irq status if they were violated.
41a7ff8297SAmit Kucheria */
42a7ff8297SAmit Kucheria struct tsens_irq_data {
43a7ff8297SAmit Kucheria u32 up_viol;
44a7ff8297SAmit Kucheria int up_thresh;
45a7ff8297SAmit Kucheria u32 up_irq_mask;
46a7ff8297SAmit Kucheria u32 up_irq_clear;
47a7ff8297SAmit Kucheria u32 low_viol;
48a7ff8297SAmit Kucheria int low_thresh;
49a7ff8297SAmit Kucheria u32 low_irq_mask;
50a7ff8297SAmit Kucheria u32 low_irq_clear;
51a7ff8297SAmit Kucheria u32 crit_viol;
52a7ff8297SAmit Kucheria u32 crit_thresh;
53a7ff8297SAmit Kucheria u32 crit_irq_mask;
54a7ff8297SAmit Kucheria u32 crit_irq_clear;
55a7ff8297SAmit Kucheria };
56a7ff8297SAmit Kucheria
qfprom_read(struct device * dev,const char * cname)57a7ff8297SAmit Kucheria char *qfprom_read(struct device *dev, const char *cname)
58a7ff8297SAmit Kucheria {
59a7ff8297SAmit Kucheria struct nvmem_cell *cell;
60a7ff8297SAmit Kucheria ssize_t data;
61a7ff8297SAmit Kucheria char *ret;
62a7ff8297SAmit Kucheria
63a7ff8297SAmit Kucheria cell = nvmem_cell_get(dev, cname);
64a7ff8297SAmit Kucheria if (IS_ERR(cell))
65a7ff8297SAmit Kucheria return ERR_CAST(cell);
66a7ff8297SAmit Kucheria
67a7ff8297SAmit Kucheria ret = nvmem_cell_read(cell, &data);
68a7ff8297SAmit Kucheria nvmem_cell_put(cell);
69a7ff8297SAmit Kucheria
70a7ff8297SAmit Kucheria return ret;
71a7ff8297SAmit Kucheria }
72a7ff8297SAmit Kucheria
tsens_read_calibration(struct tsens_priv * priv,int shift,u32 * p1,u32 * p2,bool backup)73439f2409SDmitry Baryshkov int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup)
74498d2457SDmitry Baryshkov {
75498d2457SDmitry Baryshkov u32 mode;
76498d2457SDmitry Baryshkov u32 base1, base2;
77439f2409SDmitry Baryshkov char name[] = "sXX_pY_backup"; /* s10_p1_backup */
78498d2457SDmitry Baryshkov int i, ret;
79498d2457SDmitry Baryshkov
80498d2457SDmitry Baryshkov if (priv->num_sensors > MAX_SENSORS)
81498d2457SDmitry Baryshkov return -EINVAL;
82498d2457SDmitry Baryshkov
83439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "mode%s", backup ? "_backup" : "");
84439f2409SDmitry Baryshkov if (ret < 0)
85439f2409SDmitry Baryshkov return ret;
86439f2409SDmitry Baryshkov
87439f2409SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &mode);
88498d2457SDmitry Baryshkov if (ret == -ENOENT)
89498d2457SDmitry Baryshkov dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n");
90498d2457SDmitry Baryshkov if (ret < 0)
91498d2457SDmitry Baryshkov return ret;
92498d2457SDmitry Baryshkov
93498d2457SDmitry Baryshkov dev_dbg(priv->dev, "calibration mode is %d\n", mode);
94498d2457SDmitry Baryshkov
95439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "base1%s", backup ? "_backup" : "");
96498d2457SDmitry Baryshkov if (ret < 0)
97498d2457SDmitry Baryshkov return ret;
98498d2457SDmitry Baryshkov
99439f2409SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base1);
100439f2409SDmitry Baryshkov if (ret < 0)
101439f2409SDmitry Baryshkov return ret;
102439f2409SDmitry Baryshkov
103439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "base2%s", backup ? "_backup" : "");
104439f2409SDmitry Baryshkov if (ret < 0)
105439f2409SDmitry Baryshkov return ret;
106439f2409SDmitry Baryshkov
107439f2409SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base2);
108498d2457SDmitry Baryshkov if (ret < 0)
109498d2457SDmitry Baryshkov return ret;
110498d2457SDmitry Baryshkov
111498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) {
112439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "s%d_p1%s", priv->sensor[i].hw_id,
113439f2409SDmitry Baryshkov backup ? "_backup" : "");
114498d2457SDmitry Baryshkov if (ret < 0)
115498d2457SDmitry Baryshkov return ret;
116498d2457SDmitry Baryshkov
117498d2457SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p1[i]);
118498d2457SDmitry Baryshkov if (ret)
119498d2457SDmitry Baryshkov return ret;
120498d2457SDmitry Baryshkov
121439f2409SDmitry Baryshkov ret = snprintf(name, sizeof(name), "s%d_p2%s", priv->sensor[i].hw_id,
122439f2409SDmitry Baryshkov backup ? "_backup" : "");
123498d2457SDmitry Baryshkov if (ret < 0)
124498d2457SDmitry Baryshkov return ret;
125498d2457SDmitry Baryshkov
126498d2457SDmitry Baryshkov ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p2[i]);
127498d2457SDmitry Baryshkov if (ret)
128498d2457SDmitry Baryshkov return ret;
129498d2457SDmitry Baryshkov }
130498d2457SDmitry Baryshkov
131498d2457SDmitry Baryshkov switch (mode) {
132498d2457SDmitry Baryshkov case ONE_PT_CALIB:
133498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++)
134498d2457SDmitry Baryshkov p1[i] = p1[i] + (base1 << shift);
135498d2457SDmitry Baryshkov break;
136498d2457SDmitry Baryshkov case TWO_PT_CALIB:
137b6f739daSStephan Gerhold case TWO_PT_CALIB_NO_OFFSET:
138498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++)
139498d2457SDmitry Baryshkov p2[i] = (p2[i] + base2) << shift;
140498d2457SDmitry Baryshkov fallthrough;
141498d2457SDmitry Baryshkov case ONE_PT_CALIB2:
142b6f739daSStephan Gerhold case ONE_PT_CALIB2_NO_OFFSET:
143498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++)
144498d2457SDmitry Baryshkov p1[i] = (p1[i] + base1) << shift;
145498d2457SDmitry Baryshkov break;
146498d2457SDmitry Baryshkov default:
147498d2457SDmitry Baryshkov dev_dbg(priv->dev, "calibrationless mode\n");
148498d2457SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) {
149498d2457SDmitry Baryshkov p1[i] = 500;
150498d2457SDmitry Baryshkov p2[i] = 780;
151498d2457SDmitry Baryshkov }
152498d2457SDmitry Baryshkov }
153498d2457SDmitry Baryshkov
154b6f739daSStephan Gerhold /* Apply calibration offset workaround except for _NO_OFFSET modes */
155b6f739daSStephan Gerhold switch (mode) {
156b6f739daSStephan Gerhold case TWO_PT_CALIB:
157b6f739daSStephan Gerhold for (i = 0; i < priv->num_sensors; i++)
158b6f739daSStephan Gerhold p2[i] += priv->sensor[i].p2_calib_offset;
159b6f739daSStephan Gerhold fallthrough;
160b6f739daSStephan Gerhold case ONE_PT_CALIB2:
161b6f739daSStephan Gerhold for (i = 0; i < priv->num_sensors; i++)
162b6f739daSStephan Gerhold p1[i] += priv->sensor[i].p1_calib_offset;
163b6f739daSStephan Gerhold break;
164b6f739daSStephan Gerhold }
165b6f739daSStephan Gerhold
166439f2409SDmitry Baryshkov return mode;
167439f2409SDmitry Baryshkov }
168439f2409SDmitry Baryshkov
tsens_calibrate_nvmem(struct tsens_priv * priv,int shift)169439f2409SDmitry Baryshkov int tsens_calibrate_nvmem(struct tsens_priv *priv, int shift)
170439f2409SDmitry Baryshkov {
171439f2409SDmitry Baryshkov u32 p1[MAX_SENSORS], p2[MAX_SENSORS];
172439f2409SDmitry Baryshkov int mode;
173439f2409SDmitry Baryshkov
174439f2409SDmitry Baryshkov mode = tsens_read_calibration(priv, shift, p1, p2, false);
175439f2409SDmitry Baryshkov if (mode < 0)
176439f2409SDmitry Baryshkov return mode;
177439f2409SDmitry Baryshkov
178498d2457SDmitry Baryshkov compute_intercept_slope(priv, p1, p2, mode);
179498d2457SDmitry Baryshkov
180498d2457SDmitry Baryshkov return 0;
181498d2457SDmitry Baryshkov }
182498d2457SDmitry Baryshkov
tsens_calibrate_common(struct tsens_priv * priv)183498d2457SDmitry Baryshkov int tsens_calibrate_common(struct tsens_priv *priv)
184498d2457SDmitry Baryshkov {
185498d2457SDmitry Baryshkov return tsens_calibrate_nvmem(priv, 2);
186498d2457SDmitry Baryshkov }
187498d2457SDmitry Baryshkov
tsens_read_cell(const struct tsens_single_value * cell,u8 len,u32 * data0,u32 * data1)188913d32e2SDmitry Baryshkov static u32 tsens_read_cell(const struct tsens_single_value *cell, u8 len, u32 *data0, u32 *data1)
189913d32e2SDmitry Baryshkov {
190913d32e2SDmitry Baryshkov u32 val;
191913d32e2SDmitry Baryshkov u32 *data = cell->blob ? data1 : data0;
192913d32e2SDmitry Baryshkov
193913d32e2SDmitry Baryshkov if (cell->shift + len <= 32) {
194913d32e2SDmitry Baryshkov val = data[cell->idx] >> cell->shift;
195913d32e2SDmitry Baryshkov } else {
196913d32e2SDmitry Baryshkov u8 part = 32 - cell->shift;
197913d32e2SDmitry Baryshkov
198913d32e2SDmitry Baryshkov val = data[cell->idx] >> cell->shift;
199913d32e2SDmitry Baryshkov val |= data[cell->idx + 1] << part;
200913d32e2SDmitry Baryshkov }
201913d32e2SDmitry Baryshkov
202913d32e2SDmitry Baryshkov return val & ((1 << len) - 1);
203913d32e2SDmitry Baryshkov }
204913d32e2SDmitry Baryshkov
tsens_read_calibration_legacy(struct tsens_priv * priv,const struct tsens_legacy_calibration_format * format,u32 * p1,u32 * p2,u32 * cdata0,u32 * cdata1)205913d32e2SDmitry Baryshkov int tsens_read_calibration_legacy(struct tsens_priv *priv,
206913d32e2SDmitry Baryshkov const struct tsens_legacy_calibration_format *format,
207913d32e2SDmitry Baryshkov u32 *p1, u32 *p2,
208913d32e2SDmitry Baryshkov u32 *cdata0, u32 *cdata1)
209913d32e2SDmitry Baryshkov {
210913d32e2SDmitry Baryshkov u32 mode, invalid;
211913d32e2SDmitry Baryshkov u32 base1, base2;
212913d32e2SDmitry Baryshkov int i;
213913d32e2SDmitry Baryshkov
214913d32e2SDmitry Baryshkov mode = tsens_read_cell(&format->mode, 2, cdata0, cdata1);
215913d32e2SDmitry Baryshkov invalid = tsens_read_cell(&format->invalid, 1, cdata0, cdata1);
216913d32e2SDmitry Baryshkov if (invalid)
217913d32e2SDmitry Baryshkov mode = NO_PT_CALIB;
218913d32e2SDmitry Baryshkov dev_dbg(priv->dev, "calibration mode is %d\n", mode);
219913d32e2SDmitry Baryshkov
220913d32e2SDmitry Baryshkov base1 = tsens_read_cell(&format->base[0], format->base_len, cdata0, cdata1);
221913d32e2SDmitry Baryshkov base2 = tsens_read_cell(&format->base[1], format->base_len, cdata0, cdata1);
222913d32e2SDmitry Baryshkov
223913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) {
224913d32e2SDmitry Baryshkov p1[i] = tsens_read_cell(&format->sp[i][0], format->sp_len, cdata0, cdata1);
225913d32e2SDmitry Baryshkov p2[i] = tsens_read_cell(&format->sp[i][1], format->sp_len, cdata0, cdata1);
226913d32e2SDmitry Baryshkov }
227913d32e2SDmitry Baryshkov
228913d32e2SDmitry Baryshkov switch (mode) {
229913d32e2SDmitry Baryshkov case ONE_PT_CALIB:
230913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++)
231913d32e2SDmitry Baryshkov p1[i] = p1[i] + (base1 << format->base_shift);
232913d32e2SDmitry Baryshkov break;
233913d32e2SDmitry Baryshkov case TWO_PT_CALIB:
234913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++)
235913d32e2SDmitry Baryshkov p2[i] = (p2[i] + base2) << format->base_shift;
236913d32e2SDmitry Baryshkov fallthrough;
237913d32e2SDmitry Baryshkov case ONE_PT_CALIB2:
238913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++)
239913d32e2SDmitry Baryshkov p1[i] = (p1[i] + base1) << format->base_shift;
240913d32e2SDmitry Baryshkov break;
241913d32e2SDmitry Baryshkov default:
242913d32e2SDmitry Baryshkov dev_dbg(priv->dev, "calibrationless mode\n");
243913d32e2SDmitry Baryshkov for (i = 0; i < priv->num_sensors; i++) {
244913d32e2SDmitry Baryshkov p1[i] = 500;
245913d32e2SDmitry Baryshkov p2[i] = 780;
246913d32e2SDmitry Baryshkov }
247913d32e2SDmitry Baryshkov }
248913d32e2SDmitry Baryshkov
249913d32e2SDmitry Baryshkov return mode;
250913d32e2SDmitry Baryshkov }
251913d32e2SDmitry Baryshkov
252a7ff8297SAmit Kucheria /*
253a7ff8297SAmit Kucheria * Use this function on devices where slope and offset calculations
254a7ff8297SAmit Kucheria * depend on calibration data read from qfprom. On others the slope
255a7ff8297SAmit Kucheria * and offset values are derived from tz->tzp->slope and tz->tzp->offset
256a7ff8297SAmit Kucheria * resp.
257a7ff8297SAmit Kucheria */
compute_intercept_slope(struct tsens_priv * priv,u32 * p1,u32 * p2,u32 mode)258a7ff8297SAmit Kucheria void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
259a7ff8297SAmit Kucheria u32 *p2, u32 mode)
260a7ff8297SAmit Kucheria {
261a7ff8297SAmit Kucheria int i;
262a7ff8297SAmit Kucheria int num, den;
263a7ff8297SAmit Kucheria
264a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) {
265a7ff8297SAmit Kucheria dev_dbg(priv->dev,
266a7ff8297SAmit Kucheria "%s: sensor%d - data_point1:%#x data_point2:%#x\n",
2672d5ca6e4SAleksandr Mishin __func__, i, p1[i], p2 ? p2[i] : 0);
268a7ff8297SAmit Kucheria
2699d51769bSAnsuel Smith if (!priv->sensor[i].slope)
270a7ff8297SAmit Kucheria priv->sensor[i].slope = SLOPE_DEFAULT;
271b6f739daSStephan Gerhold if (mode == TWO_PT_CALIB || mode == TWO_PT_CALIB_NO_OFFSET) {
272a7ff8297SAmit Kucheria /*
273a7ff8297SAmit Kucheria * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
274a7ff8297SAmit Kucheria * temp_120_degc - temp_30_degc (x2 - x1)
275a7ff8297SAmit Kucheria */
276a7ff8297SAmit Kucheria num = p2[i] - p1[i];
277a7ff8297SAmit Kucheria num *= SLOPE_FACTOR;
278a7ff8297SAmit Kucheria den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
279a7ff8297SAmit Kucheria priv->sensor[i].slope = num / den;
280a7ff8297SAmit Kucheria }
281a7ff8297SAmit Kucheria
282a7ff8297SAmit Kucheria priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
283a7ff8297SAmit Kucheria (CAL_DEGC_PT1 *
284a7ff8297SAmit Kucheria priv->sensor[i].slope);
285a7ff8297SAmit Kucheria dev_dbg(priv->dev, "%s: offset:%d\n", __func__,
286a7ff8297SAmit Kucheria priv->sensor[i].offset);
287a7ff8297SAmit Kucheria }
288a7ff8297SAmit Kucheria }
289a7ff8297SAmit Kucheria
degc_to_code(int degc,const struct tsens_sensor * s)290a7ff8297SAmit Kucheria static inline u32 degc_to_code(int degc, const struct tsens_sensor *s)
291a7ff8297SAmit Kucheria {
292a7ff8297SAmit Kucheria u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR);
293a7ff8297SAmit Kucheria
294a7ff8297SAmit Kucheria pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc);
295a7ff8297SAmit Kucheria return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE);
296a7ff8297SAmit Kucheria }
297a7ff8297SAmit Kucheria
code_to_degc(u32 adc_code,const struct tsens_sensor * s)298a7ff8297SAmit Kucheria static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
299a7ff8297SAmit Kucheria {
300a7ff8297SAmit Kucheria int degc, num, den;
301a7ff8297SAmit Kucheria
302a7ff8297SAmit Kucheria num = (adc_code * SLOPE_FACTOR) - s->offset;
303a7ff8297SAmit Kucheria den = s->slope;
304a7ff8297SAmit Kucheria
305a7ff8297SAmit Kucheria if (num > 0)
306a7ff8297SAmit Kucheria degc = num + (den / 2);
307a7ff8297SAmit Kucheria else if (num < 0)
308a7ff8297SAmit Kucheria degc = num - (den / 2);
309a7ff8297SAmit Kucheria else
310a7ff8297SAmit Kucheria degc = num;
311a7ff8297SAmit Kucheria
312a7ff8297SAmit Kucheria degc /= den;
313a7ff8297SAmit Kucheria
314a7ff8297SAmit Kucheria return degc;
315a7ff8297SAmit Kucheria }
316a7ff8297SAmit Kucheria
317a7ff8297SAmit Kucheria /**
318a7ff8297SAmit Kucheria * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
319a7ff8297SAmit Kucheria * @s: Pointer to sensor struct
320a7ff8297SAmit Kucheria * @field: Index into regmap_field array pointing to temperature data
321a7ff8297SAmit Kucheria *
322a7ff8297SAmit Kucheria * This function handles temperature returned in ADC code or deciCelsius
323a7ff8297SAmit Kucheria * depending on IP version.
324a7ff8297SAmit Kucheria *
325a7ff8297SAmit Kucheria * Return: Temperature in milliCelsius on success, a negative errno will
326a7ff8297SAmit Kucheria * be returned in error cases
327a7ff8297SAmit Kucheria */
tsens_hw_to_mC(const struct tsens_sensor * s,int field)328a7ff8297SAmit Kucheria static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
329a7ff8297SAmit Kucheria {
330a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv;
331a7ff8297SAmit Kucheria u32 resolution;
332a7ff8297SAmit Kucheria u32 temp = 0;
333a7ff8297SAmit Kucheria int ret;
334a7ff8297SAmit Kucheria
335a7ff8297SAmit Kucheria resolution = priv->fields[LAST_TEMP_0].msb -
336a7ff8297SAmit Kucheria priv->fields[LAST_TEMP_0].lsb;
337a7ff8297SAmit Kucheria
338a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[field], &temp);
339a7ff8297SAmit Kucheria if (ret)
340a7ff8297SAmit Kucheria return ret;
341a7ff8297SAmit Kucheria
342a7ff8297SAmit Kucheria /* Convert temperature from ADC code to milliCelsius */
343a7ff8297SAmit Kucheria if (priv->feat->adc)
344a7ff8297SAmit Kucheria return code_to_degc(temp, s) * 1000;
345a7ff8297SAmit Kucheria
346a7ff8297SAmit Kucheria /* deciCelsius -> milliCelsius along with sign extension */
347a7ff8297SAmit Kucheria return sign_extend32(temp, resolution) * 100;
348a7ff8297SAmit Kucheria }
349a7ff8297SAmit Kucheria
350a7ff8297SAmit Kucheria /**
351a7ff8297SAmit Kucheria * tsens_mC_to_hw - Convert temperature to hardware register value
352a7ff8297SAmit Kucheria * @s: Pointer to sensor struct
353a7ff8297SAmit Kucheria * @temp: temperature in milliCelsius to be programmed to hardware
354a7ff8297SAmit Kucheria *
355a7ff8297SAmit Kucheria * This function outputs the value to be written to hardware in ADC code
356a7ff8297SAmit Kucheria * or deciCelsius depending on IP version.
357a7ff8297SAmit Kucheria *
358a7ff8297SAmit Kucheria * Return: ADC code or temperature in deciCelsius.
359a7ff8297SAmit Kucheria */
tsens_mC_to_hw(const struct tsens_sensor * s,int temp)360a7ff8297SAmit Kucheria static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
361a7ff8297SAmit Kucheria {
362a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv;
363a7ff8297SAmit Kucheria
364a7ff8297SAmit Kucheria /* milliC to adc code */
365a7ff8297SAmit Kucheria if (priv->feat->adc)
366a7ff8297SAmit Kucheria return degc_to_code(temp / 1000, s);
367a7ff8297SAmit Kucheria
368a7ff8297SAmit Kucheria /* milliC to deciC */
369a7ff8297SAmit Kucheria return temp / 100;
370a7ff8297SAmit Kucheria }
371a7ff8297SAmit Kucheria
tsens_version(struct tsens_priv * priv)372a7ff8297SAmit Kucheria static inline enum tsens_ver tsens_version(struct tsens_priv *priv)
373a7ff8297SAmit Kucheria {
374a7ff8297SAmit Kucheria return priv->feat->ver_major;
375a7ff8297SAmit Kucheria }
376a7ff8297SAmit Kucheria
tsens_set_interrupt_v1(struct tsens_priv * priv,u32 hw_id,enum tsens_irq_type irq_type,bool enable)377a7ff8297SAmit Kucheria static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
378a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable)
379a7ff8297SAmit Kucheria {
380a7ff8297SAmit Kucheria u32 index = 0;
381a7ff8297SAmit Kucheria
382a7ff8297SAmit Kucheria switch (irq_type) {
383a7ff8297SAmit Kucheria case UPPER:
384a7ff8297SAmit Kucheria index = UP_INT_CLEAR_0 + hw_id;
385a7ff8297SAmit Kucheria break;
386a7ff8297SAmit Kucheria case LOWER:
387a7ff8297SAmit Kucheria index = LOW_INT_CLEAR_0 + hw_id;
388a7ff8297SAmit Kucheria break;
389a7ff8297SAmit Kucheria case CRITICAL:
390a7ff8297SAmit Kucheria /* No critical interrupts before v2 */
391a7ff8297SAmit Kucheria return;
392a7ff8297SAmit Kucheria }
393a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index], enable ? 0 : 1);
394a7ff8297SAmit Kucheria }
395a7ff8297SAmit Kucheria
tsens_set_interrupt_v2(struct tsens_priv * priv,u32 hw_id,enum tsens_irq_type irq_type,bool enable)396a7ff8297SAmit Kucheria static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
397a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable)
398a7ff8297SAmit Kucheria {
399a7ff8297SAmit Kucheria u32 index_mask = 0, index_clear = 0;
400a7ff8297SAmit Kucheria
401a7ff8297SAmit Kucheria /*
402a7ff8297SAmit Kucheria * To enable the interrupt flag for a sensor:
403a7ff8297SAmit Kucheria * - clear the mask bit
404a7ff8297SAmit Kucheria * To disable the interrupt flag for a sensor:
405a7ff8297SAmit Kucheria * - Mask further interrupts for this sensor
406a7ff8297SAmit Kucheria * - Write 1 followed by 0 to clear the interrupt
407a7ff8297SAmit Kucheria */
408a7ff8297SAmit Kucheria switch (irq_type) {
409a7ff8297SAmit Kucheria case UPPER:
410a7ff8297SAmit Kucheria index_mask = UP_INT_MASK_0 + hw_id;
411a7ff8297SAmit Kucheria index_clear = UP_INT_CLEAR_0 + hw_id;
412a7ff8297SAmit Kucheria break;
413a7ff8297SAmit Kucheria case LOWER:
414a7ff8297SAmit Kucheria index_mask = LOW_INT_MASK_0 + hw_id;
415a7ff8297SAmit Kucheria index_clear = LOW_INT_CLEAR_0 + hw_id;
416a7ff8297SAmit Kucheria break;
417a7ff8297SAmit Kucheria case CRITICAL:
418a7ff8297SAmit Kucheria index_mask = CRIT_INT_MASK_0 + hw_id;
419a7ff8297SAmit Kucheria index_clear = CRIT_INT_CLEAR_0 + hw_id;
420a7ff8297SAmit Kucheria break;
421a7ff8297SAmit Kucheria }
422a7ff8297SAmit Kucheria
423a7ff8297SAmit Kucheria if (enable) {
424a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_mask], 0);
425a7ff8297SAmit Kucheria } else {
426a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_mask], 1);
427a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_clear], 1);
428a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_clear], 0);
429a7ff8297SAmit Kucheria }
430a7ff8297SAmit Kucheria }
431a7ff8297SAmit Kucheria
432a7ff8297SAmit Kucheria /**
433a7ff8297SAmit Kucheria * tsens_set_interrupt - Set state of an interrupt
434a7ff8297SAmit Kucheria * @priv: Pointer to tsens controller private data
435a7ff8297SAmit Kucheria * @hw_id: Hardware ID aka. sensor number
436a7ff8297SAmit Kucheria * @irq_type: irq_type from enum tsens_irq_type
437a7ff8297SAmit Kucheria * @enable: false = disable, true = enable
438a7ff8297SAmit Kucheria *
439a7ff8297SAmit Kucheria * Call IP-specific function to set state of an interrupt
440a7ff8297SAmit Kucheria *
441a7ff8297SAmit Kucheria * Return: void
442a7ff8297SAmit Kucheria */
tsens_set_interrupt(struct tsens_priv * priv,u32 hw_id,enum tsens_irq_type irq_type,bool enable)443a7ff8297SAmit Kucheria static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
444a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable)
445a7ff8297SAmit Kucheria {
446a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
447a7ff8297SAmit Kucheria irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
448a7ff8297SAmit Kucheria enable ? "en" : "dis");
449a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X)
450a7ff8297SAmit Kucheria tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
451a7ff8297SAmit Kucheria else
452a7ff8297SAmit Kucheria tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
453a7ff8297SAmit Kucheria }
454a7ff8297SAmit Kucheria
455a7ff8297SAmit Kucheria /**
456a7ff8297SAmit Kucheria * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
457a7ff8297SAmit Kucheria * @priv: Pointer to tsens controller private data
458a7ff8297SAmit Kucheria * @hw_id: Hardware ID aka. sensor number
459a7ff8297SAmit Kucheria * @d: Pointer to irq state data
460a7ff8297SAmit Kucheria *
461a7ff8297SAmit Kucheria * Return: 0 if threshold was not violated, 1 if it was violated and negative
462a7ff8297SAmit Kucheria * errno in case of errors
463a7ff8297SAmit Kucheria */
tsens_threshold_violated(struct tsens_priv * priv,u32 hw_id,struct tsens_irq_data * d)464a7ff8297SAmit Kucheria static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
465a7ff8297SAmit Kucheria struct tsens_irq_data *d)
466a7ff8297SAmit Kucheria {
467a7ff8297SAmit Kucheria int ret;
468a7ff8297SAmit Kucheria
469a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
470a7ff8297SAmit Kucheria if (ret)
471a7ff8297SAmit Kucheria return ret;
472a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
473a7ff8297SAmit Kucheria if (ret)
474a7ff8297SAmit Kucheria return ret;
475a7ff8297SAmit Kucheria
476a7ff8297SAmit Kucheria if (priv->feat->crit_int) {
477a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
478a7ff8297SAmit Kucheria &d->crit_viol);
479a7ff8297SAmit Kucheria if (ret)
480a7ff8297SAmit Kucheria return ret;
481a7ff8297SAmit Kucheria }
482a7ff8297SAmit Kucheria
483a7ff8297SAmit Kucheria if (d->up_viol || d->low_viol || d->crit_viol)
484a7ff8297SAmit Kucheria return 1;
485a7ff8297SAmit Kucheria
486a7ff8297SAmit Kucheria return 0;
487a7ff8297SAmit Kucheria }
488a7ff8297SAmit Kucheria
tsens_read_irq_state(struct tsens_priv * priv,u32 hw_id,const struct tsens_sensor * s,struct tsens_irq_data * d)489a7ff8297SAmit Kucheria static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
490a7ff8297SAmit Kucheria const struct tsens_sensor *s,
491a7ff8297SAmit Kucheria struct tsens_irq_data *d)
492a7ff8297SAmit Kucheria {
493a7ff8297SAmit Kucheria int ret;
494a7ff8297SAmit Kucheria
495a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
496a7ff8297SAmit Kucheria if (ret)
497a7ff8297SAmit Kucheria return ret;
498a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
499a7ff8297SAmit Kucheria if (ret)
500a7ff8297SAmit Kucheria return ret;
501a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X) {
502a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
503a7ff8297SAmit Kucheria if (ret)
504a7ff8297SAmit Kucheria return ret;
505a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
506a7ff8297SAmit Kucheria if (ret)
507a7ff8297SAmit Kucheria return ret;
508a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
509a7ff8297SAmit Kucheria &d->crit_irq_clear);
510a7ff8297SAmit Kucheria if (ret)
511a7ff8297SAmit Kucheria return ret;
512a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
513a7ff8297SAmit Kucheria &d->crit_irq_mask);
514a7ff8297SAmit Kucheria if (ret)
515a7ff8297SAmit Kucheria return ret;
516a7ff8297SAmit Kucheria
517a7ff8297SAmit Kucheria d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
518a7ff8297SAmit Kucheria } else {
519a7ff8297SAmit Kucheria /* No mask register on older TSENS */
520a7ff8297SAmit Kucheria d->up_irq_mask = 0;
521a7ff8297SAmit Kucheria d->low_irq_mask = 0;
522a7ff8297SAmit Kucheria d->crit_irq_clear = 0;
523a7ff8297SAmit Kucheria d->crit_irq_mask = 0;
524a7ff8297SAmit Kucheria d->crit_thresh = 0;
525a7ff8297SAmit Kucheria }
526a7ff8297SAmit Kucheria
527a7ff8297SAmit Kucheria d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
528a7ff8297SAmit Kucheria d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
529a7ff8297SAmit Kucheria
530a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
531a7ff8297SAmit Kucheria hw_id, __func__,
532a7ff8297SAmit Kucheria (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
533a7ff8297SAmit Kucheria d->low_viol, d->up_viol, d->crit_viol,
534a7ff8297SAmit Kucheria d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
535a7ff8297SAmit Kucheria d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
536a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
537a7ff8297SAmit Kucheria (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
538a7ff8297SAmit Kucheria d->low_thresh, d->up_thresh, d->crit_thresh);
539a7ff8297SAmit Kucheria
540a7ff8297SAmit Kucheria return 0;
541a7ff8297SAmit Kucheria }
542a7ff8297SAmit Kucheria
masked_irq(u32 hw_id,u32 mask,enum tsens_ver ver)543a7ff8297SAmit Kucheria static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
544a7ff8297SAmit Kucheria {
545a7ff8297SAmit Kucheria if (ver > VER_1_X)
546a7ff8297SAmit Kucheria return mask & (1 << hw_id);
547a7ff8297SAmit Kucheria
548a7ff8297SAmit Kucheria /* v1, v0.1 don't have a irq mask register */
549a7ff8297SAmit Kucheria return 0;
550a7ff8297SAmit Kucheria }
551a7ff8297SAmit Kucheria
552a7ff8297SAmit Kucheria /**
553a7ff8297SAmit Kucheria * tsens_critical_irq_thread() - Threaded handler for critical interrupts
554a7ff8297SAmit Kucheria * @irq: irq number
555a7ff8297SAmit Kucheria * @data: tsens controller private data
556a7ff8297SAmit Kucheria *
557a7ff8297SAmit Kucheria * Check FSM watchdog bark status and clear if needed.
558a7ff8297SAmit Kucheria * Check all sensors to find ones that violated their critical threshold limits.
559a7ff8297SAmit Kucheria * Clear and then re-enable the interrupt.
560a7ff8297SAmit Kucheria *
561a7ff8297SAmit Kucheria * The level-triggered interrupt might deassert if the temperature returned to
562a7ff8297SAmit Kucheria * within the threshold limits by the time the handler got scheduled. We
563a7ff8297SAmit Kucheria * consider the irq to have been handled in that case.
564a7ff8297SAmit Kucheria *
565a7ff8297SAmit Kucheria * Return: IRQ_HANDLED
566a7ff8297SAmit Kucheria */
tsens_critical_irq_thread(int irq,void * data)5673ecc8292SAmit Kucheria static irqreturn_t tsens_critical_irq_thread(int irq, void *data)
568a7ff8297SAmit Kucheria {
569a7ff8297SAmit Kucheria struct tsens_priv *priv = data;
570a7ff8297SAmit Kucheria struct tsens_irq_data d;
571a7ff8297SAmit Kucheria int temp, ret, i;
572a7ff8297SAmit Kucheria u32 wdog_status, wdog_count;
573a7ff8297SAmit Kucheria
574a7ff8297SAmit Kucheria if (priv->feat->has_watchdog) {
575a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
576a7ff8297SAmit Kucheria &wdog_status);
577a7ff8297SAmit Kucheria if (ret)
578a7ff8297SAmit Kucheria return ret;
579a7ff8297SAmit Kucheria
580a7ff8297SAmit Kucheria if (wdog_status) {
581a7ff8297SAmit Kucheria /* Clear WDOG interrupt */
582a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
583a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
584a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
585a7ff8297SAmit Kucheria &wdog_count);
586a7ff8297SAmit Kucheria if (ret)
587a7ff8297SAmit Kucheria return ret;
588a7ff8297SAmit Kucheria if (wdog_count)
589a7ff8297SAmit Kucheria dev_dbg(priv->dev, "%s: watchdog count: %d\n",
590a7ff8297SAmit Kucheria __func__, wdog_count);
591a7ff8297SAmit Kucheria
592a7ff8297SAmit Kucheria /* Fall through to handle critical interrupts if any */
593a7ff8297SAmit Kucheria }
594a7ff8297SAmit Kucheria }
595a7ff8297SAmit Kucheria
596a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) {
597a7ff8297SAmit Kucheria const struct tsens_sensor *s = &priv->sensor[i];
598a7ff8297SAmit Kucheria u32 hw_id = s->hw_id;
599a7ff8297SAmit Kucheria
600cf969218SAnsuel Smith if (!s->tzd)
601a7ff8297SAmit Kucheria continue;
602a7ff8297SAmit Kucheria if (!tsens_threshold_violated(priv, hw_id, &d))
603a7ff8297SAmit Kucheria continue;
604a7ff8297SAmit Kucheria ret = get_temp_tsens_valid(s, &temp);
605a7ff8297SAmit Kucheria if (ret) {
606a7ff8297SAmit Kucheria dev_err(priv->dev, "[%u] %s: error reading sensor\n",
607a7ff8297SAmit Kucheria hw_id, __func__);
608a7ff8297SAmit Kucheria continue;
609a7ff8297SAmit Kucheria }
610a7ff8297SAmit Kucheria
611a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d);
612a7ff8297SAmit Kucheria if (d.crit_viol &&
613a7ff8297SAmit Kucheria !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
614a7ff8297SAmit Kucheria /* Mask critical interrupts, unused on Linux */
615a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, CRITICAL, false);
616a7ff8297SAmit Kucheria }
617a7ff8297SAmit Kucheria }
618a7ff8297SAmit Kucheria
619a7ff8297SAmit Kucheria return IRQ_HANDLED;
620a7ff8297SAmit Kucheria }
621a7ff8297SAmit Kucheria
622a7ff8297SAmit Kucheria /**
623a7ff8297SAmit Kucheria * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
624a7ff8297SAmit Kucheria * @irq: irq number
625a7ff8297SAmit Kucheria * @data: tsens controller private data
626a7ff8297SAmit Kucheria *
627a7ff8297SAmit Kucheria * Check all sensors to find ones that violated their threshold limits. If the
628a7ff8297SAmit Kucheria * temperature is still outside the limits, call thermal_zone_device_update() to
629a7ff8297SAmit Kucheria * update the thresholds, else re-enable the interrupts.
630a7ff8297SAmit Kucheria *
631a7ff8297SAmit Kucheria * The level-triggered interrupt might deassert if the temperature returned to
632a7ff8297SAmit Kucheria * within the threshold limits by the time the handler got scheduled. We
633a7ff8297SAmit Kucheria * consider the irq to have been handled in that case.
634a7ff8297SAmit Kucheria *
635a7ff8297SAmit Kucheria * Return: IRQ_HANDLED
636a7ff8297SAmit Kucheria */
tsens_irq_thread(int irq,void * data)6373ecc8292SAmit Kucheria static irqreturn_t tsens_irq_thread(int irq, void *data)
638a7ff8297SAmit Kucheria {
639a7ff8297SAmit Kucheria struct tsens_priv *priv = data;
640a7ff8297SAmit Kucheria struct tsens_irq_data d;
641df715f26SDaniel Lezcano int i;
642a7ff8297SAmit Kucheria
643a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) {
644a7ff8297SAmit Kucheria const struct tsens_sensor *s = &priv->sensor[i];
645a7ff8297SAmit Kucheria u32 hw_id = s->hw_id;
646a7ff8297SAmit Kucheria
647cf969218SAnsuel Smith if (!s->tzd)
648a7ff8297SAmit Kucheria continue;
649a7ff8297SAmit Kucheria if (!tsens_threshold_violated(priv, hw_id, &d))
650a7ff8297SAmit Kucheria continue;
651a7ff8297SAmit Kucheria
652df715f26SDaniel Lezcano thermal_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED);
65353e2a20eSAnsuel Smith
65453e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) {
65553e2a20eSAnsuel Smith /* Constraint: There is only 1 interrupt control register for all
65653e2a20eSAnsuel Smith * 11 temperature sensor. So monitoring more than 1 sensor based
65753e2a20eSAnsuel Smith * on interrupts will yield inconsistent result. To overcome this
65853e2a20eSAnsuel Smith * issue we will monitor only sensor 0 which is the master sensor.
65953e2a20eSAnsuel Smith */
66053e2a20eSAnsuel Smith break;
66153e2a20eSAnsuel Smith }
662a7ff8297SAmit Kucheria }
663a7ff8297SAmit Kucheria
664a7ff8297SAmit Kucheria return IRQ_HANDLED;
665a7ff8297SAmit Kucheria }
666a7ff8297SAmit Kucheria
6674360af35SRobert Marko /**
6684360af35SRobert Marko * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts
6694360af35SRobert Marko * @irq: irq number
6704360af35SRobert Marko * @data: tsens controller private data
6714360af35SRobert Marko *
6724360af35SRobert Marko * Handle the combined interrupt as if it were 2 separate interrupts, so call the
6734360af35SRobert Marko * critical handler first and then the up/low one.
6744360af35SRobert Marko *
6754360af35SRobert Marko * Return: IRQ_HANDLED
6764360af35SRobert Marko */
tsens_combined_irq_thread(int irq,void * data)6774360af35SRobert Marko static irqreturn_t tsens_combined_irq_thread(int irq, void *data)
6784360af35SRobert Marko {
6794360af35SRobert Marko irqreturn_t ret;
6804360af35SRobert Marko
6814360af35SRobert Marko ret = tsens_critical_irq_thread(irq, data);
6824360af35SRobert Marko if (ret != IRQ_HANDLED)
6834360af35SRobert Marko return ret;
6844360af35SRobert Marko
6854360af35SRobert Marko return tsens_irq_thread(irq, data);
6864360af35SRobert Marko }
6874360af35SRobert Marko
tsens_set_trips(struct thermal_zone_device * tz,int low,int high)688ca1b9a9eSDaniel Lezcano static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high)
689a7ff8297SAmit Kucheria {
6905f68d078SDaniel Lezcano struct tsens_sensor *s = thermal_zone_device_priv(tz);
691a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv;
692a7ff8297SAmit Kucheria struct device *dev = priv->dev;
693a7ff8297SAmit Kucheria struct tsens_irq_data d;
694a7ff8297SAmit Kucheria unsigned long flags;
695a7ff8297SAmit Kucheria int high_val, low_val, cl_high, cl_low;
696a7ff8297SAmit Kucheria u32 hw_id = s->hw_id;
697a7ff8297SAmit Kucheria
69853e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) {
69953e2a20eSAnsuel Smith /* Pre v0.1 IP had a single register for each type of interrupt
70053e2a20eSAnsuel Smith * and thresholds
70153e2a20eSAnsuel Smith */
70253e2a20eSAnsuel Smith hw_id = 0;
70353e2a20eSAnsuel Smith }
70453e2a20eSAnsuel Smith
705a7ff8297SAmit Kucheria dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
706a7ff8297SAmit Kucheria hw_id, __func__, low, high);
707a7ff8297SAmit Kucheria
708f63bacedSRobert Marko cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp);
709f63bacedSRobert Marko cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp);
710a7ff8297SAmit Kucheria
711a7ff8297SAmit Kucheria high_val = tsens_mC_to_hw(s, cl_high);
712a7ff8297SAmit Kucheria low_val = tsens_mC_to_hw(s, cl_low);
713a7ff8297SAmit Kucheria
714a7ff8297SAmit Kucheria spin_lock_irqsave(&priv->ul_lock, flags);
715a7ff8297SAmit Kucheria
716a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d);
717a7ff8297SAmit Kucheria
718a7ff8297SAmit Kucheria /* Write the new thresholds and clear the status */
719a7ff8297SAmit Kucheria regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
720a7ff8297SAmit Kucheria regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
721a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, LOWER, true);
722a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, UPPER, true);
723a7ff8297SAmit Kucheria
724a7ff8297SAmit Kucheria spin_unlock_irqrestore(&priv->ul_lock, flags);
725a7ff8297SAmit Kucheria
726a7ff8297SAmit Kucheria dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
727a7ff8297SAmit Kucheria hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
728a7ff8297SAmit Kucheria
729a7ff8297SAmit Kucheria return 0;
730a7ff8297SAmit Kucheria }
731a7ff8297SAmit Kucheria
tsens_enable_irq(struct tsens_priv * priv)7323ecc8292SAmit Kucheria static int tsens_enable_irq(struct tsens_priv *priv)
733a7ff8297SAmit Kucheria {
734a7ff8297SAmit Kucheria int ret;
735a7ff8297SAmit Kucheria int val = tsens_version(priv) > VER_1_X ? 7 : 1;
736a7ff8297SAmit Kucheria
737a7ff8297SAmit Kucheria ret = regmap_field_write(priv->rf[INT_EN], val);
738a7ff8297SAmit Kucheria if (ret < 0)
739a7ff8297SAmit Kucheria dev_err(priv->dev, "%s: failed to enable interrupts\n",
740a7ff8297SAmit Kucheria __func__);
741a7ff8297SAmit Kucheria
742a7ff8297SAmit Kucheria return ret;
743a7ff8297SAmit Kucheria }
744a7ff8297SAmit Kucheria
tsens_disable_irq(struct tsens_priv * priv)7453ecc8292SAmit Kucheria static void tsens_disable_irq(struct tsens_priv *priv)
746a7ff8297SAmit Kucheria {
747a7ff8297SAmit Kucheria regmap_field_write(priv->rf[INT_EN], 0);
748a7ff8297SAmit Kucheria }
749a7ff8297SAmit Kucheria
get_temp_tsens_valid(const struct tsens_sensor * s,int * temp)750a7ff8297SAmit Kucheria int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
751a7ff8297SAmit Kucheria {
752a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv;
753a7ff8297SAmit Kucheria int hw_id = s->hw_id;
754a7ff8297SAmit Kucheria u32 temp_idx = LAST_TEMP_0 + hw_id;
755a7ff8297SAmit Kucheria u32 valid_idx = VALID_0 + hw_id;
756a7ff8297SAmit Kucheria u32 valid;
757a7ff8297SAmit Kucheria int ret;
758a7ff8297SAmit Kucheria
75953e2a20eSAnsuel Smith /* VER_0 doesn't have VALID bit */
760d012f918SAnsuel Smith if (tsens_version(priv) == VER_0)
761d012f918SAnsuel Smith goto get_temp;
762d012f918SAnsuel Smith
763a7ff8297SAmit Kucheria /* Valid bit is 0 for 6 AHB clock cycles.
764a7ff8297SAmit Kucheria * At 19.2MHz, 1 AHB clock is ~60ns.
765a7ff8297SAmit Kucheria * We should enter this loop very, very rarely.
766d012f918SAnsuel Smith * Wait 1 us since it's the min of poll_timeout macro.
767d012f918SAnsuel Smith * Old value was 400 ns.
768a7ff8297SAmit Kucheria */
769d012f918SAnsuel Smith ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid,
770d012f918SAnsuel Smith valid, 1, 20 * USEC_PER_MSEC);
771a7ff8297SAmit Kucheria if (ret)
772a7ff8297SAmit Kucheria return ret;
773a7ff8297SAmit Kucheria
774d012f918SAnsuel Smith get_temp:
775a7ff8297SAmit Kucheria /* Valid bit is set, OK to read the temperature */
776a7ff8297SAmit Kucheria *temp = tsens_hw_to_mC(s, temp_idx);
777a7ff8297SAmit Kucheria
778a7ff8297SAmit Kucheria return 0;
779a7ff8297SAmit Kucheria }
780a7ff8297SAmit Kucheria
get_temp_common(const struct tsens_sensor * s,int * temp)781a7ff8297SAmit Kucheria int get_temp_common(const struct tsens_sensor *s, int *temp)
782a7ff8297SAmit Kucheria {
783a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv;
784a7ff8297SAmit Kucheria int hw_id = s->hw_id;
78553e2a20eSAnsuel Smith int last_temp = 0, ret, trdy;
78653e2a20eSAnsuel Smith unsigned long timeout;
78753e2a20eSAnsuel Smith
78853e2a20eSAnsuel Smith timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
78953e2a20eSAnsuel Smith do {
79053e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) {
79153e2a20eSAnsuel Smith ret = regmap_field_read(priv->rf[TRDY], &trdy);
79253e2a20eSAnsuel Smith if (ret)
79353e2a20eSAnsuel Smith return ret;
79453e2a20eSAnsuel Smith if (!trdy)
79553e2a20eSAnsuel Smith continue;
79653e2a20eSAnsuel Smith }
797a7ff8297SAmit Kucheria
798a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
799a7ff8297SAmit Kucheria if (ret)
800a7ff8297SAmit Kucheria return ret;
801a7ff8297SAmit Kucheria
802a7ff8297SAmit Kucheria *temp = code_to_degc(last_temp, s) * 1000;
803a7ff8297SAmit Kucheria
804a7ff8297SAmit Kucheria return 0;
80553e2a20eSAnsuel Smith } while (time_before(jiffies, timeout));
80653e2a20eSAnsuel Smith
80753e2a20eSAnsuel Smith return -ETIMEDOUT;
808a7ff8297SAmit Kucheria }
809a7ff8297SAmit Kucheria
810a7ff8297SAmit Kucheria #ifdef CONFIG_DEBUG_FS
dbg_sensors_show(struct seq_file * s,void * data)811a7ff8297SAmit Kucheria static int dbg_sensors_show(struct seq_file *s, void *data)
812a7ff8297SAmit Kucheria {
813a7ff8297SAmit Kucheria struct platform_device *pdev = s->private;
814a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev);
815a7ff8297SAmit Kucheria int i;
816a7ff8297SAmit Kucheria
817a7ff8297SAmit Kucheria seq_printf(s, "max: %2d\nnum: %2d\n\n",
818a7ff8297SAmit Kucheria priv->feat->max_sensors, priv->num_sensors);
819a7ff8297SAmit Kucheria
820a7ff8297SAmit Kucheria seq_puts(s, " id slope offset\n--------------------------\n");
821a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) {
822a7ff8297SAmit Kucheria seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id,
823a7ff8297SAmit Kucheria priv->sensor[i].slope, priv->sensor[i].offset);
824a7ff8297SAmit Kucheria }
825a7ff8297SAmit Kucheria
826a7ff8297SAmit Kucheria return 0;
827a7ff8297SAmit Kucheria }
828a7ff8297SAmit Kucheria
dbg_version_show(struct seq_file * s,void * data)829a7ff8297SAmit Kucheria static int dbg_version_show(struct seq_file *s, void *data)
830a7ff8297SAmit Kucheria {
831a7ff8297SAmit Kucheria struct platform_device *pdev = s->private;
832a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev);
833a7ff8297SAmit Kucheria u32 maj_ver, min_ver, step_ver;
834a7ff8297SAmit Kucheria int ret;
835a7ff8297SAmit Kucheria
836a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_0_1) {
837a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver);
838a7ff8297SAmit Kucheria if (ret)
839a7ff8297SAmit Kucheria return ret;
840a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver);
841a7ff8297SAmit Kucheria if (ret)
842a7ff8297SAmit Kucheria return ret;
843a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_STEP], &step_ver);
844a7ff8297SAmit Kucheria if (ret)
845a7ff8297SAmit Kucheria return ret;
846a7ff8297SAmit Kucheria seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
847a7ff8297SAmit Kucheria } else {
848c7e077e9SChristian Marangi seq_printf(s, "0.%d.0\n", priv->feat->ver_major);
849a7ff8297SAmit Kucheria }
850a7ff8297SAmit Kucheria
851a7ff8297SAmit Kucheria return 0;
852a7ff8297SAmit Kucheria }
853a7ff8297SAmit Kucheria
854a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_version);
855a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_sensors);
856a7ff8297SAmit Kucheria
tsens_debug_init(struct platform_device * pdev)857a7ff8297SAmit Kucheria static void tsens_debug_init(struct platform_device *pdev)
858a7ff8297SAmit Kucheria {
859a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev);
860a7ff8297SAmit Kucheria
86189992d95SChristian Marangi priv->debug_root = debugfs_lookup("tsens", NULL);
86289992d95SChristian Marangi if (!priv->debug_root)
863a7ff8297SAmit Kucheria priv->debug_root = debugfs_create_dir("tsens", NULL);
864a7ff8297SAmit Kucheria
865a7ff8297SAmit Kucheria /* A directory for each instance of the TSENS IP */
866a7ff8297SAmit Kucheria priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
86789992d95SChristian Marangi debugfs_create_file("version", 0444, priv->debug, pdev, &dbg_version_fops);
868a7ff8297SAmit Kucheria debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
869a7ff8297SAmit Kucheria }
870a7ff8297SAmit Kucheria #else
tsens_debug_init(struct platform_device * pdev)871a7ff8297SAmit Kucheria static inline void tsens_debug_init(struct platform_device *pdev) {}
872a7ff8297SAmit Kucheria #endif
873a7ff8297SAmit Kucheria
874a7ff8297SAmit Kucheria static const struct regmap_config tsens_config = {
875a7ff8297SAmit Kucheria .name = "tm",
876a7ff8297SAmit Kucheria .reg_bits = 32,
877a7ff8297SAmit Kucheria .val_bits = 32,
878a7ff8297SAmit Kucheria .reg_stride = 4,
879a7ff8297SAmit Kucheria };
880a7ff8297SAmit Kucheria
881a7ff8297SAmit Kucheria static const struct regmap_config tsens_srot_config = {
882a7ff8297SAmit Kucheria .name = "srot",
883a7ff8297SAmit Kucheria .reg_bits = 32,
884a7ff8297SAmit Kucheria .val_bits = 32,
885a7ff8297SAmit Kucheria .reg_stride = 4,
886a7ff8297SAmit Kucheria };
887a7ff8297SAmit Kucheria
init_common(struct tsens_priv * priv)888a7ff8297SAmit Kucheria int __init init_common(struct tsens_priv *priv)
889a7ff8297SAmit Kucheria {
890a7ff8297SAmit Kucheria void __iomem *tm_base, *srot_base;
891a7ff8297SAmit Kucheria struct device *dev = priv->dev;
892a7ff8297SAmit Kucheria u32 ver_minor;
893a7ff8297SAmit Kucheria struct resource *res;
894a7ff8297SAmit Kucheria u32 enabled;
895a7ff8297SAmit Kucheria int ret, i, j;
896a7ff8297SAmit Kucheria struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
897a7ff8297SAmit Kucheria
898a7ff8297SAmit Kucheria if (!op)
899a7ff8297SAmit Kucheria return -EINVAL;
900a7ff8297SAmit Kucheria
901a7ff8297SAmit Kucheria if (op->num_resources > 1) {
902a7ff8297SAmit Kucheria /* DT with separate SROT and TM address space */
903a7ff8297SAmit Kucheria priv->tm_offset = 0;
904a7ff8297SAmit Kucheria res = platform_get_resource(op, IORESOURCE_MEM, 1);
905a7ff8297SAmit Kucheria srot_base = devm_ioremap_resource(dev, res);
906a7ff8297SAmit Kucheria if (IS_ERR(srot_base)) {
907a7ff8297SAmit Kucheria ret = PTR_ERR(srot_base);
908a7ff8297SAmit Kucheria goto err_put_device;
909a7ff8297SAmit Kucheria }
910a7ff8297SAmit Kucheria
911a7ff8297SAmit Kucheria priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
912a7ff8297SAmit Kucheria &tsens_srot_config);
913a7ff8297SAmit Kucheria if (IS_ERR(priv->srot_map)) {
914a7ff8297SAmit Kucheria ret = PTR_ERR(priv->srot_map);
915a7ff8297SAmit Kucheria goto err_put_device;
916a7ff8297SAmit Kucheria }
917a7ff8297SAmit Kucheria } else {
918a7ff8297SAmit Kucheria /* old DTs where SROT and TM were in a contiguous 2K block */
919a7ff8297SAmit Kucheria priv->tm_offset = 0x1000;
920a7ff8297SAmit Kucheria }
921a7ff8297SAmit Kucheria
92253e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1) {
923a7ff8297SAmit Kucheria res = platform_get_resource(op, IORESOURCE_MEM, 0);
924a7ff8297SAmit Kucheria tm_base = devm_ioremap_resource(dev, res);
925a7ff8297SAmit Kucheria if (IS_ERR(tm_base)) {
926a7ff8297SAmit Kucheria ret = PTR_ERR(tm_base);
927a7ff8297SAmit Kucheria goto err_put_device;
928a7ff8297SAmit Kucheria }
929a7ff8297SAmit Kucheria
930a7ff8297SAmit Kucheria priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
93153e2a20eSAnsuel Smith } else { /* VER_0 share the same gcc regs using a syscon */
93253e2a20eSAnsuel Smith struct device *parent = priv->dev->parent;
93353e2a20eSAnsuel Smith
93453e2a20eSAnsuel Smith if (parent)
93553e2a20eSAnsuel Smith priv->tm_map = syscon_node_to_regmap(parent->of_node);
93653e2a20eSAnsuel Smith }
93753e2a20eSAnsuel Smith
93853e2a20eSAnsuel Smith if (IS_ERR_OR_NULL(priv->tm_map)) {
93953e2a20eSAnsuel Smith if (!priv->tm_map)
94053e2a20eSAnsuel Smith ret = -ENODEV;
94153e2a20eSAnsuel Smith else
942a7ff8297SAmit Kucheria ret = PTR_ERR(priv->tm_map);
943a7ff8297SAmit Kucheria goto err_put_device;
944a7ff8297SAmit Kucheria }
945a7ff8297SAmit Kucheria
94653e2a20eSAnsuel Smith /* VER_0 have only tm_map */
94753e2a20eSAnsuel Smith if (!priv->srot_map)
94853e2a20eSAnsuel Smith priv->srot_map = priv->tm_map;
94953e2a20eSAnsuel Smith
950a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_0_1) {
951a7ff8297SAmit Kucheria for (i = VER_MAJOR; i <= VER_STEP; i++) {
952a7ff8297SAmit Kucheria priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
953a7ff8297SAmit Kucheria priv->fields[i]);
954f4136863SGuangqing Zhu if (IS_ERR(priv->rf[i])) {
955f4136863SGuangqing Zhu ret = PTR_ERR(priv->rf[i]);
956f4136863SGuangqing Zhu goto err_put_device;
957f4136863SGuangqing Zhu }
958a7ff8297SAmit Kucheria }
959a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
960a7ff8297SAmit Kucheria if (ret)
961a7ff8297SAmit Kucheria goto err_put_device;
962a7ff8297SAmit Kucheria }
963a7ff8297SAmit Kucheria
964a7ff8297SAmit Kucheria priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
965a7ff8297SAmit Kucheria priv->fields[TSENS_EN]);
966a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[TSENS_EN])) {
967a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[TSENS_EN]);
968a7ff8297SAmit Kucheria goto err_put_device;
969a7ff8297SAmit Kucheria }
97053e2a20eSAnsuel Smith /* in VER_0 TSENS need to be explicitly enabled */
97153e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0)
97253e2a20eSAnsuel Smith regmap_field_write(priv->rf[TSENS_EN], 1);
97353e2a20eSAnsuel Smith
974a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
975a7ff8297SAmit Kucheria if (ret)
976a7ff8297SAmit Kucheria goto err_put_device;
977a7ff8297SAmit Kucheria if (!enabled) {
978a7ff8297SAmit Kucheria dev_err(dev, "%s: device not enabled\n", __func__);
979a7ff8297SAmit Kucheria ret = -ENODEV;
980a7ff8297SAmit Kucheria goto err_put_device;
981a7ff8297SAmit Kucheria }
982a7ff8297SAmit Kucheria
983a7ff8297SAmit Kucheria priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
984a7ff8297SAmit Kucheria priv->fields[SENSOR_EN]);
985a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[SENSOR_EN])) {
986a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[SENSOR_EN]);
987a7ff8297SAmit Kucheria goto err_put_device;
988a7ff8297SAmit Kucheria }
989a7ff8297SAmit Kucheria priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
990a7ff8297SAmit Kucheria priv->fields[INT_EN]);
991a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[INT_EN])) {
992a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[INT_EN]);
993a7ff8297SAmit Kucheria goto err_put_device;
994a7ff8297SAmit Kucheria }
995a7ff8297SAmit Kucheria
99653e2a20eSAnsuel Smith priv->rf[TSENS_SW_RST] =
99753e2a20eSAnsuel Smith devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]);
99853e2a20eSAnsuel Smith if (IS_ERR(priv->rf[TSENS_SW_RST])) {
99953e2a20eSAnsuel Smith ret = PTR_ERR(priv->rf[TSENS_SW_RST]);
100053e2a20eSAnsuel Smith goto err_put_device;
100153e2a20eSAnsuel Smith }
100253e2a20eSAnsuel Smith
100353e2a20eSAnsuel Smith priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]);
100453e2a20eSAnsuel Smith if (IS_ERR(priv->rf[TRDY])) {
100553e2a20eSAnsuel Smith ret = PTR_ERR(priv->rf[TRDY]);
100653e2a20eSAnsuel Smith goto err_put_device;
100753e2a20eSAnsuel Smith }
100853e2a20eSAnsuel Smith
1009a7ff8297SAmit Kucheria /* This loop might need changes if enum regfield_ids is reordered */
1010a7ff8297SAmit Kucheria for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
1011a7ff8297SAmit Kucheria for (i = 0; i < priv->feat->max_sensors; i++) {
1012a7ff8297SAmit Kucheria int idx = j + i;
1013a7ff8297SAmit Kucheria
1014a7ff8297SAmit Kucheria priv->rf[idx] = devm_regmap_field_alloc(dev,
1015a7ff8297SAmit Kucheria priv->tm_map,
1016a7ff8297SAmit Kucheria priv->fields[idx]);
1017a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[idx])) {
1018a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[idx]);
1019a7ff8297SAmit Kucheria goto err_put_device;
1020a7ff8297SAmit Kucheria }
1021a7ff8297SAmit Kucheria }
1022a7ff8297SAmit Kucheria }
1023a7ff8297SAmit Kucheria
102453e2a20eSAnsuel Smith if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) {
1025a7ff8297SAmit Kucheria /* Loop might need changes if enum regfield_ids is reordered */
1026a7ff8297SAmit Kucheria for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
1027a7ff8297SAmit Kucheria for (i = 0; i < priv->feat->max_sensors; i++) {
1028a7ff8297SAmit Kucheria int idx = j + i;
1029a7ff8297SAmit Kucheria
1030a7ff8297SAmit Kucheria priv->rf[idx] =
1031a7ff8297SAmit Kucheria devm_regmap_field_alloc(dev,
1032a7ff8297SAmit Kucheria priv->tm_map,
1033a7ff8297SAmit Kucheria priv->fields[idx]);
1034a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[idx])) {
1035a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[idx]);
1036a7ff8297SAmit Kucheria goto err_put_device;
1037a7ff8297SAmit Kucheria }
1038a7ff8297SAmit Kucheria }
1039a7ff8297SAmit Kucheria }
1040a7ff8297SAmit Kucheria }
1041a7ff8297SAmit Kucheria
1042a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X && ver_minor > 2) {
1043a7ff8297SAmit Kucheria /* Watchdog is present only on v2.3+ */
1044a7ff8297SAmit Kucheria priv->feat->has_watchdog = 1;
1045a7ff8297SAmit Kucheria for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
1046a7ff8297SAmit Kucheria priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
1047a7ff8297SAmit Kucheria priv->fields[i]);
1048a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[i])) {
1049a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[i]);
1050a7ff8297SAmit Kucheria goto err_put_device;
1051a7ff8297SAmit Kucheria }
1052a7ff8297SAmit Kucheria }
1053a7ff8297SAmit Kucheria /*
1054a7ff8297SAmit Kucheria * Watchdog is already enabled, unmask the bark.
1055a7ff8297SAmit Kucheria * Disable cycle completion monitoring
1056a7ff8297SAmit Kucheria */
1057a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
1058a7ff8297SAmit Kucheria regmap_field_write(priv->rf[CC_MON_MASK], 1);
1059a7ff8297SAmit Kucheria }
1060a7ff8297SAmit Kucheria
1061a7ff8297SAmit Kucheria spin_lock_init(&priv->ul_lock);
106253e2a20eSAnsuel Smith
106353e2a20eSAnsuel Smith /* VER_0 interrupt doesn't need to be enabled */
106453e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1)
1065a7ff8297SAmit Kucheria tsens_enable_irq(priv);
106653e2a20eSAnsuel Smith
1067a7ff8297SAmit Kucheria err_put_device:
1068a7ff8297SAmit Kucheria put_device(&op->dev);
1069a7ff8297SAmit Kucheria return ret;
1070a7ff8297SAmit Kucheria }
1071a7ff8297SAmit Kucheria
tsens_get_temp(struct thermal_zone_device * tz,int * temp)1072ca1b9a9eSDaniel Lezcano static int tsens_get_temp(struct thermal_zone_device *tz, int *temp)
10739066073cSRajendra Nayak {
10745f68d078SDaniel Lezcano struct tsens_sensor *s = thermal_zone_device_priv(tz);
107569b628acSAmit Kucheria struct tsens_priv *priv = s->priv;
10769066073cSRajendra Nayak
10778b71bce4SAmit Kucheria return priv->ops->get_temp(s, temp);
10789066073cSRajendra Nayak }
10799066073cSRajendra Nayak
tsens_suspend(struct device * dev)10805b97469aSArnd Bergmann static int __maybe_unused tsens_suspend(struct device *dev)
10819066073cSRajendra Nayak {
108269b628acSAmit Kucheria struct tsens_priv *priv = dev_get_drvdata(dev);
10839066073cSRajendra Nayak
108469b628acSAmit Kucheria if (priv->ops && priv->ops->suspend)
108569b628acSAmit Kucheria return priv->ops->suspend(priv);
10869066073cSRajendra Nayak
10879066073cSRajendra Nayak return 0;
10889066073cSRajendra Nayak }
10899066073cSRajendra Nayak
tsens_resume(struct device * dev)10905b97469aSArnd Bergmann static int __maybe_unused tsens_resume(struct device *dev)
10919066073cSRajendra Nayak {
109269b628acSAmit Kucheria struct tsens_priv *priv = dev_get_drvdata(dev);
10939066073cSRajendra Nayak
109469b628acSAmit Kucheria if (priv->ops && priv->ops->resume)
109569b628acSAmit Kucheria return priv->ops->resume(priv);
10969066073cSRajendra Nayak
10979066073cSRajendra Nayak return 0;
10989066073cSRajendra Nayak }
10999066073cSRajendra Nayak
11009066073cSRajendra Nayak static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
11019066073cSRajendra Nayak
11029066073cSRajendra Nayak static const struct of_device_id tsens_table[] = {
11039066073cSRajendra Nayak {
11046b3aeafbSAnsuel Smith .compatible = "qcom,ipq8064-tsens",
11056b3aeafbSAnsuel Smith .data = &data_8960,
11066b3aeafbSAnsuel Smith }, {
11076840455dSRobert Marko .compatible = "qcom,ipq8074-tsens",
11086840455dSRobert Marko .data = &data_ipq8074,
11096840455dSRobert Marko }, {
1110a2149ab8SKonrad Dybcio .compatible = "qcom,mdm9607-tsens",
1111a2149ab8SKonrad Dybcio .data = &data_9607,
1112a2149ab8SKonrad Dybcio }, {
1113598e1afcSMatti Lehtimäki .compatible = "qcom,msm8226-tsens",
1114598e1afcSMatti Lehtimäki .data = &data_8226,
1115598e1afcSMatti Lehtimäki }, {
11164af164c1SStephan Gerhold .compatible = "qcom,msm8909-tsens",
11174af164c1SStephan Gerhold .data = &data_8909,
11184af164c1SStephan Gerhold }, {
11199066073cSRajendra Nayak .compatible = "qcom,msm8916-tsens",
1120840a5bd3SRajendra Nayak .data = &data_8916,
11219066073cSRajendra Nayak }, {
1122*9e4828b7SBarnabás Czémán .compatible = "qcom,msm8937-tsens",
1123*9e4828b7SBarnabás Czémán .data = &data_8937,
1124*9e4828b7SBarnabás Czémán }, {
1125332bc8ebSShawn Guo .compatible = "qcom,msm8939-tsens",
1126332bc8ebSShawn Guo .data = &data_8939,
1127332bc8ebSShawn Guo }, {
1128a7d3006bSDmitry Baryshkov .compatible = "qcom,msm8956-tsens",
1129a7d3006bSDmitry Baryshkov .data = &data_8956,
1130a7d3006bSDmitry Baryshkov }, {
11312caf7396SDmitry Baryshkov .compatible = "qcom,msm8960-tsens",
11322caf7396SDmitry Baryshkov .data = &data_8960,
11332caf7396SDmitry Baryshkov }, {
11349066073cSRajendra Nayak .compatible = "qcom,msm8974-tsens",
11355e6703bdSRajendra Nayak .data = &data_8974,
1136d059c739SRajendra Nayak }, {
11370e580290SAngeloGioacchino Del Regno .compatible = "qcom,msm8976-tsens",
11380e580290SAngeloGioacchino Del Regno .data = &data_8976,
11390e580290SAngeloGioacchino Del Regno }, {
1140d059c739SRajendra Nayak .compatible = "qcom,msm8996-tsens",
1141d059c739SRajendra Nayak .data = &data_8996,
1142191dc74bSAmit Kucheria }, {
1143e8c24c6fSAmit Kucheria .compatible = "qcom,tsens-v1",
1144e8c24c6fSAmit Kucheria .data = &data_tsens_v1,
1145e8c24c6fSAmit Kucheria }, {
1146191dc74bSAmit Kucheria .compatible = "qcom,tsens-v2",
1147191dc74bSAmit Kucheria .data = &data_tsens_v2,
11489066073cSRajendra Nayak },
11499066073cSRajendra Nayak {}
11509066073cSRajendra Nayak };
11519066073cSRajendra Nayak MODULE_DEVICE_TABLE(of, tsens_table);
11529066073cSRajendra Nayak
1153ca1b9a9eSDaniel Lezcano static const struct thermal_zone_device_ops tsens_of_ops = {
11549066073cSRajendra Nayak .get_temp = tsens_get_temp,
1155634e11d5SAmit Kucheria .set_trips = tsens_set_trips,
11569066073cSRajendra Nayak };
11579066073cSRajendra Nayak
tsens_register_irq(struct tsens_priv * priv,char * irqname,irq_handler_t thread_fn)115879125e03SAmit Kucheria static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
115979125e03SAmit Kucheria irq_handler_t thread_fn)
116079125e03SAmit Kucheria {
116179125e03SAmit Kucheria struct platform_device *pdev;
116279125e03SAmit Kucheria int ret, irq;
116379125e03SAmit Kucheria
116479125e03SAmit Kucheria pdev = of_find_device_by_node(priv->dev->of_node);
116579125e03SAmit Kucheria if (!pdev)
116679125e03SAmit Kucheria return -ENODEV;
116779125e03SAmit Kucheria
116879125e03SAmit Kucheria irq = platform_get_irq_byname(pdev, irqname);
116979125e03SAmit Kucheria if (irq < 0) {
117079125e03SAmit Kucheria ret = irq;
117179125e03SAmit Kucheria /* For old DTs with no IRQ defined */
117279125e03SAmit Kucheria if (irq == -ENXIO)
117379125e03SAmit Kucheria ret = 0;
117479125e03SAmit Kucheria } else {
117553e2a20eSAnsuel Smith /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */
117653e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0)
117779125e03SAmit Kucheria ret = devm_request_threaded_irq(&pdev->dev, irq,
117853e2a20eSAnsuel Smith thread_fn, NULL,
117953e2a20eSAnsuel Smith IRQF_TRIGGER_RISING,
118053e2a20eSAnsuel Smith dev_name(&pdev->dev),
118153e2a20eSAnsuel Smith priv);
118253e2a20eSAnsuel Smith else
118353e2a20eSAnsuel Smith ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
118453e2a20eSAnsuel Smith thread_fn, IRQF_ONESHOT,
118553e2a20eSAnsuel Smith dev_name(&pdev->dev),
118653e2a20eSAnsuel Smith priv);
118753e2a20eSAnsuel Smith
118879125e03SAmit Kucheria if (ret)
118979125e03SAmit Kucheria dev_err(&pdev->dev, "%s: failed to get irq\n",
119079125e03SAmit Kucheria __func__);
119179125e03SAmit Kucheria else
119279125e03SAmit Kucheria enable_irq_wake(irq);
119379125e03SAmit Kucheria }
119479125e03SAmit Kucheria
119579125e03SAmit Kucheria put_device(&pdev->dev);
119679125e03SAmit Kucheria return ret;
119779125e03SAmit Kucheria }
119879125e03SAmit Kucheria
tsens_register(struct tsens_priv * priv)119969b628acSAmit Kucheria static int tsens_register(struct tsens_priv *priv)
12009066073cSRajendra Nayak {
120179125e03SAmit Kucheria int i, ret;
12029066073cSRajendra Nayak struct thermal_zone_device *tzd;
12039066073cSRajendra Nayak
120469b628acSAmit Kucheria for (i = 0; i < priv->num_sensors; i++) {
120569b628acSAmit Kucheria priv->sensor[i].priv = priv;
1206ca1b9a9eSDaniel Lezcano tzd = devm_thermal_of_zone_register(priv->dev, priv->sensor[i].hw_id,
120769b628acSAmit Kucheria &priv->sensor[i],
12089066073cSRajendra Nayak &tsens_of_ops);
12099066073cSRajendra Nayak if (IS_ERR(tzd))
12109066073cSRajendra Nayak continue;
121169b628acSAmit Kucheria priv->sensor[i].tzd = tzd;
121269b628acSAmit Kucheria if (priv->ops->enable)
121369b628acSAmit Kucheria priv->ops->enable(priv, i);
12148556e19dSDmitry Baryshkov
12157adbbb3bSYangtao Li devm_thermal_add_hwmon_sysfs(priv->dev, tzd);
12169066073cSRajendra Nayak }
1217634e11d5SAmit Kucheria
121853e2a20eSAnsuel Smith /* VER_0 require to set MIN and MAX THRESH
121953e2a20eSAnsuel Smith * These 2 regs are set using the:
122053e2a20eSAnsuel Smith * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C
122153e2a20eSAnsuel Smith * - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C
122253e2a20eSAnsuel Smith */
122353e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) {
122453e2a20eSAnsuel Smith regmap_field_write(priv->rf[CRIT_THRESH_0],
122553e2a20eSAnsuel Smith tsens_mC_to_hw(priv->sensor, 120000));
122653e2a20eSAnsuel Smith
122753e2a20eSAnsuel Smith regmap_field_write(priv->rf[CRIT_THRESH_1],
122853e2a20eSAnsuel Smith tsens_mC_to_hw(priv->sensor, 0));
122953e2a20eSAnsuel Smith }
123053e2a20eSAnsuel Smith
12314360af35SRobert Marko if (priv->feat->combo_int) {
12324360af35SRobert Marko ret = tsens_register_irq(priv, "combined",
12334360af35SRobert Marko tsens_combined_irq_thread);
12344360af35SRobert Marko } else {
123579125e03SAmit Kucheria ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
123679125e03SAmit Kucheria if (ret < 0)
123779125e03SAmit Kucheria return ret;
1238634e11d5SAmit Kucheria
123979125e03SAmit Kucheria if (priv->feat->crit_int)
124079125e03SAmit Kucheria ret = tsens_register_irq(priv, "critical",
124179125e03SAmit Kucheria tsens_critical_irq_thread);
12424360af35SRobert Marko }
1243634e11d5SAmit Kucheria
1244634e11d5SAmit Kucheria return ret;
12459066073cSRajendra Nayak }
12469066073cSRajendra Nayak
tsens_probe(struct platform_device * pdev)12479066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev)
12489066073cSRajendra Nayak {
12499066073cSRajendra Nayak int ret, i;
12509066073cSRajendra Nayak struct device *dev;
12519066073cSRajendra Nayak struct device_node *np;
125269b628acSAmit Kucheria struct tsens_priv *priv;
12533c040ce0SAmit Kucheria const struct tsens_plat_data *data;
12549066073cSRajendra Nayak const struct of_device_id *id;
12556d7c70d1SBjorn Andersson u32 num_sensors;
12569066073cSRajendra Nayak
12579066073cSRajendra Nayak if (pdev->dev.of_node)
12589066073cSRajendra Nayak dev = &pdev->dev;
12599066073cSRajendra Nayak else
12609066073cSRajendra Nayak dev = pdev->dev.parent;
12619066073cSRajendra Nayak
12629066073cSRajendra Nayak np = dev->of_node;
12639066073cSRajendra Nayak
12649066073cSRajendra Nayak id = of_match_node(tsens_table, np);
126520d4fd84SRajendra Nayak if (id)
12669066073cSRajendra Nayak data = id->data;
126720d4fd84SRajendra Nayak else
126820d4fd84SRajendra Nayak data = &data_8960;
12699066073cSRajendra Nayak
12706d7c70d1SBjorn Andersson num_sensors = data->num_sensors;
12716d7c70d1SBjorn Andersson
12726d7c70d1SBjorn Andersson if (np)
12736d7c70d1SBjorn Andersson of_property_read_u32(np, "#qcom,sensors", &num_sensors);
12746d7c70d1SBjorn Andersson
12756d7c70d1SBjorn Andersson if (num_sensors <= 0) {
12763795ad5eSAmit Kucheria dev_err(dev, "%s: invalid number of sensors\n", __func__);
12779066073cSRajendra Nayak return -EINVAL;
12789066073cSRajendra Nayak }
12799066073cSRajendra Nayak
128069b628acSAmit Kucheria priv = devm_kzalloc(dev,
128169b628acSAmit Kucheria struct_size(priv, sensor, num_sensors),
12820ed2dd03SKees Cook GFP_KERNEL);
128369b628acSAmit Kucheria if (!priv)
12849066073cSRajendra Nayak return -ENOMEM;
12859066073cSRajendra Nayak
128669b628acSAmit Kucheria priv->dev = dev;
128769b628acSAmit Kucheria priv->num_sensors = num_sensors;
128869b628acSAmit Kucheria priv->ops = data->ops;
128969b628acSAmit Kucheria for (i = 0; i < priv->num_sensors; i++) {
12909066073cSRajendra Nayak if (data->hw_ids)
129169b628acSAmit Kucheria priv->sensor[i].hw_id = data->hw_ids[i];
12929066073cSRajendra Nayak else
129369b628acSAmit Kucheria priv->sensor[i].hw_id = i;
12949066073cSRajendra Nayak }
1295c1997054SAmit Kucheria priv->feat = data->feat;
1296c1997054SAmit Kucheria priv->fields = data->fields;
12979066073cSRajendra Nayak
12980e9c0bc7SAmit Kucheria platform_set_drvdata(pdev, priv);
12990e9c0bc7SAmit Kucheria
130069b628acSAmit Kucheria if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
13019066073cSRajendra Nayak return -EINVAL;
13029066073cSRajendra Nayak
130369b628acSAmit Kucheria ret = priv->ops->init(priv);
13049066073cSRajendra Nayak if (ret < 0) {
13053795ad5eSAmit Kucheria dev_err(dev, "%s: init failed\n", __func__);
13069066073cSRajendra Nayak return ret;
13079066073cSRajendra Nayak }
13089066073cSRajendra Nayak
130969b628acSAmit Kucheria if (priv->ops->calibrate) {
131069b628acSAmit Kucheria ret = priv->ops->calibrate(priv);
13119066073cSRajendra Nayak if (ret < 0) {
1312fc7d18cfSAmit Kucheria if (ret != -EPROBE_DEFER)
13133795ad5eSAmit Kucheria dev_err(dev, "%s: calibration failed\n", __func__);
13149066073cSRajendra Nayak return ret;
13159066073cSRajendra Nayak }
13169066073cSRajendra Nayak }
13179066073cSRajendra Nayak
1318de48d876SChristian Marangi ret = tsens_register(priv);
1319de48d876SChristian Marangi if (!ret)
1320de48d876SChristian Marangi tsens_debug_init(pdev);
1321de48d876SChristian Marangi
1322de48d876SChristian Marangi return ret;
13239066073cSRajendra Nayak }
13249066073cSRajendra Nayak
tsens_remove(struct platform_device * pdev)13259066073cSRajendra Nayak static int tsens_remove(struct platform_device *pdev)
13269066073cSRajendra Nayak {
132769b628acSAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev);
13289066073cSRajendra Nayak
13297c938f48SAmit Kucheria debugfs_remove_recursive(priv->debug_root);
1330634e11d5SAmit Kucheria tsens_disable_irq(priv);
133169b628acSAmit Kucheria if (priv->ops->disable)
133269b628acSAmit Kucheria priv->ops->disable(priv);
13339066073cSRajendra Nayak
13349066073cSRajendra Nayak return 0;
13359066073cSRajendra Nayak }
13369066073cSRajendra Nayak
13379066073cSRajendra Nayak static struct platform_driver tsens_driver = {
13389066073cSRajendra Nayak .probe = tsens_probe,
13399066073cSRajendra Nayak .remove = tsens_remove,
13409066073cSRajendra Nayak .driver = {
13419066073cSRajendra Nayak .name = "qcom-tsens",
13429066073cSRajendra Nayak .pm = &tsens_pm_ops,
13439066073cSRajendra Nayak .of_match_table = tsens_table,
13449066073cSRajendra Nayak },
13459066073cSRajendra Nayak };
13469066073cSRajendra Nayak module_platform_driver(tsens_driver);
13479066073cSRajendra Nayak
13489066073cSRajendra Nayak MODULE_LICENSE("GPL v2");
13499066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
13509066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens");
1351