Commit | Line | Data |
---|---|---|
2d71d8de | 1 | // SPDX-License-Identifier: GPL-2.0 |
9066073c RN |
2 | /* |
3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | |
a7ff8297 | 4 | * Copyright (c) 2019, 2020, Linaro Ltd. |
9066073c RN |
5 | */ |
6 | ||
7c938f48 | 7 | #include <linux/debugfs.h> |
9066073c | 8 | #include <linux/err.h> |
a7ff8297 | 9 | #include <linux/io.h> |
9066073c | 10 | #include <linux/module.h> |
a7ff8297 | 11 | #include <linux/nvmem-consumer.h> |
9066073c | 12 | #include <linux/of.h> |
a7ff8297 | 13 | #include <linux/of_address.h> |
634e11d5 | 14 | #include <linux/of_platform.h> |
53e2a20e | 15 | #include <linux/mfd/syscon.h> |
9066073c RN |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm.h> | |
a7ff8297 | 18 | #include <linux/regmap.h> |
9066073c RN |
19 | #include <linux/slab.h> |
20 | #include <linux/thermal.h> | |
8556e19d | 21 | #include "../thermal_hwmon.h" |
9066073c RN |
22 | #include "tsens.h" |
23 | ||
a7ff8297 AK |
24 | /** |
25 | * struct tsens_irq_data - IRQ status and temperature violations | |
26 | * @up_viol: upper threshold violated | |
27 | * @up_thresh: upper threshold temperature value | |
28 | * @up_irq_mask: mask register for upper threshold irqs | |
29 | * @up_irq_clear: clear register for uppper threshold irqs | |
30 | * @low_viol: lower threshold violated | |
31 | * @low_thresh: lower threshold temperature value | |
32 | * @low_irq_mask: mask register for lower threshold irqs | |
33 | * @low_irq_clear: clear register for lower threshold irqs | |
34 | * @crit_viol: critical threshold violated | |
35 | * @crit_thresh: critical threshold temperature value | |
36 | * @crit_irq_mask: mask register for critical threshold irqs | |
37 | * @crit_irq_clear: clear register for critical threshold irqs | |
38 | * | |
39 | * Structure containing data about temperature threshold settings and | |
40 | * irq status if they were violated. | |
41 | */ | |
42 | struct tsens_irq_data { | |
43 | u32 up_viol; | |
44 | int up_thresh; | |
45 | u32 up_irq_mask; | |
46 | u32 up_irq_clear; | |
47 | u32 low_viol; | |
48 | int low_thresh; | |
49 | u32 low_irq_mask; | |
50 | u32 low_irq_clear; | |
51 | u32 crit_viol; | |
52 | u32 crit_thresh; | |
53 | u32 crit_irq_mask; | |
54 | u32 crit_irq_clear; | |
55 | }; | |
56 | ||
57 | char *qfprom_read(struct device *dev, const char *cname) | |
58 | { | |
59 | struct nvmem_cell *cell; | |
60 | ssize_t data; | |
61 | char *ret; | |
62 | ||
63 | cell = nvmem_cell_get(dev, cname); | |
64 | if (IS_ERR(cell)) | |
65 | return ERR_CAST(cell); | |
66 | ||
67 | ret = nvmem_cell_read(cell, &data); | |
68 | nvmem_cell_put(cell); | |
69 | ||
70 | return ret; | |
71 | } | |
72 | ||
73 | /* | |
74 | * Use this function on devices where slope and offset calculations | |
75 | * depend on calibration data read from qfprom. On others the slope | |
76 | * and offset values are derived from tz->tzp->slope and tz->tzp->offset | |
77 | * resp. | |
78 | */ | |
79 | void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, | |
80 | u32 *p2, u32 mode) | |
81 | { | |
82 | int i; | |
83 | int num, den; | |
84 | ||
85 | for (i = 0; i < priv->num_sensors; i++) { | |
86 | dev_dbg(priv->dev, | |
87 | "%s: sensor%d - data_point1:%#x data_point2:%#x\n", | |
88 | __func__, i, p1[i], p2[i]); | |
89 | ||
9d51769b AS |
90 | if (!priv->sensor[i].slope) |
91 | priv->sensor[i].slope = SLOPE_DEFAULT; | |
a7ff8297 AK |
92 | if (mode == TWO_PT_CALIB) { |
93 | /* | |
94 | * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ | |
95 | * temp_120_degc - temp_30_degc (x2 - x1) | |
96 | */ | |
97 | num = p2[i] - p1[i]; | |
98 | num *= SLOPE_FACTOR; | |
99 | den = CAL_DEGC_PT2 - CAL_DEGC_PT1; | |
100 | priv->sensor[i].slope = num / den; | |
101 | } | |
102 | ||
103 | priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - | |
104 | (CAL_DEGC_PT1 * | |
105 | priv->sensor[i].slope); | |
106 | dev_dbg(priv->dev, "%s: offset:%d\n", __func__, | |
107 | priv->sensor[i].offset); | |
108 | } | |
109 | } | |
110 | ||
111 | static inline u32 degc_to_code(int degc, const struct tsens_sensor *s) | |
112 | { | |
113 | u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR); | |
114 | ||
115 | pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc); | |
116 | return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE); | |
117 | } | |
118 | ||
119 | static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) | |
120 | { | |
121 | int degc, num, den; | |
122 | ||
123 | num = (adc_code * SLOPE_FACTOR) - s->offset; | |
124 | den = s->slope; | |
125 | ||
126 | if (num > 0) | |
127 | degc = num + (den / 2); | |
128 | else if (num < 0) | |
129 | degc = num - (den / 2); | |
130 | else | |
131 | degc = num; | |
132 | ||
133 | degc /= den; | |
134 | ||
135 | return degc; | |
136 | } | |
137 | ||
138 | /** | |
139 | * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. | |
140 | * @s: Pointer to sensor struct | |
141 | * @field: Index into regmap_field array pointing to temperature data | |
142 | * | |
143 | * This function handles temperature returned in ADC code or deciCelsius | |
144 | * depending on IP version. | |
145 | * | |
146 | * Return: Temperature in milliCelsius on success, a negative errno will | |
147 | * be returned in error cases | |
148 | */ | |
149 | static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) | |
150 | { | |
151 | struct tsens_priv *priv = s->priv; | |
152 | u32 resolution; | |
153 | u32 temp = 0; | |
154 | int ret; | |
155 | ||
156 | resolution = priv->fields[LAST_TEMP_0].msb - | |
157 | priv->fields[LAST_TEMP_0].lsb; | |
158 | ||
159 | ret = regmap_field_read(priv->rf[field], &temp); | |
160 | if (ret) | |
161 | return ret; | |
162 | ||
163 | /* Convert temperature from ADC code to milliCelsius */ | |
164 | if (priv->feat->adc) | |
165 | return code_to_degc(temp, s) * 1000; | |
166 | ||
167 | /* deciCelsius -> milliCelsius along with sign extension */ | |
168 | return sign_extend32(temp, resolution) * 100; | |
169 | } | |
170 | ||
171 | /** | |
172 | * tsens_mC_to_hw - Convert temperature to hardware register value | |
173 | * @s: Pointer to sensor struct | |
174 | * @temp: temperature in milliCelsius to be programmed to hardware | |
175 | * | |
176 | * This function outputs the value to be written to hardware in ADC code | |
177 | * or deciCelsius depending on IP version. | |
178 | * | |
179 | * Return: ADC code or temperature in deciCelsius. | |
180 | */ | |
181 | static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp) | |
182 | { | |
183 | struct tsens_priv *priv = s->priv; | |
184 | ||
185 | /* milliC to adc code */ | |
186 | if (priv->feat->adc) | |
187 | return degc_to_code(temp / 1000, s); | |
188 | ||
189 | /* milliC to deciC */ | |
190 | return temp / 100; | |
191 | } | |
192 | ||
193 | static inline enum tsens_ver tsens_version(struct tsens_priv *priv) | |
194 | { | |
195 | return priv->feat->ver_major; | |
196 | } | |
197 | ||
198 | static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, | |
199 | enum tsens_irq_type irq_type, bool enable) | |
200 | { | |
201 | u32 index = 0; | |
202 | ||
203 | switch (irq_type) { | |
204 | case UPPER: | |
205 | index = UP_INT_CLEAR_0 + hw_id; | |
206 | break; | |
207 | case LOWER: | |
208 | index = LOW_INT_CLEAR_0 + hw_id; | |
209 | break; | |
210 | case CRITICAL: | |
211 | /* No critical interrupts before v2 */ | |
212 | return; | |
213 | } | |
214 | regmap_field_write(priv->rf[index], enable ? 0 : 1); | |
215 | } | |
216 | ||
217 | static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id, | |
218 | enum tsens_irq_type irq_type, bool enable) | |
219 | { | |
220 | u32 index_mask = 0, index_clear = 0; | |
221 | ||
222 | /* | |
223 | * To enable the interrupt flag for a sensor: | |
224 | * - clear the mask bit | |
225 | * To disable the interrupt flag for a sensor: | |
226 | * - Mask further interrupts for this sensor | |
227 | * - Write 1 followed by 0 to clear the interrupt | |
228 | */ | |
229 | switch (irq_type) { | |
230 | case UPPER: | |
231 | index_mask = UP_INT_MASK_0 + hw_id; | |
232 | index_clear = UP_INT_CLEAR_0 + hw_id; | |
233 | break; | |
234 | case LOWER: | |
235 | index_mask = LOW_INT_MASK_0 + hw_id; | |
236 | index_clear = LOW_INT_CLEAR_0 + hw_id; | |
237 | break; | |
238 | case CRITICAL: | |
239 | index_mask = CRIT_INT_MASK_0 + hw_id; | |
240 | index_clear = CRIT_INT_CLEAR_0 + hw_id; | |
241 | break; | |
242 | } | |
243 | ||
244 | if (enable) { | |
245 | regmap_field_write(priv->rf[index_mask], 0); | |
246 | } else { | |
247 | regmap_field_write(priv->rf[index_mask], 1); | |
248 | regmap_field_write(priv->rf[index_clear], 1); | |
249 | regmap_field_write(priv->rf[index_clear], 0); | |
250 | } | |
251 | } | |
252 | ||
253 | /** | |
254 | * tsens_set_interrupt - Set state of an interrupt | |
255 | * @priv: Pointer to tsens controller private data | |
256 | * @hw_id: Hardware ID aka. sensor number | |
257 | * @irq_type: irq_type from enum tsens_irq_type | |
258 | * @enable: false = disable, true = enable | |
259 | * | |
260 | * Call IP-specific function to set state of an interrupt | |
261 | * | |
262 | * Return: void | |
263 | */ | |
264 | static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id, | |
265 | enum tsens_irq_type irq_type, bool enable) | |
266 | { | |
267 | dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__, | |
268 | irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW", | |
269 | enable ? "en" : "dis"); | |
270 | if (tsens_version(priv) > VER_1_X) | |
271 | tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); | |
272 | else | |
273 | tsens_set_interrupt_v1(priv, hw_id, irq_type, enable); | |
274 | } | |
275 | ||
276 | /** | |
277 | * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold | |
278 | * @priv: Pointer to tsens controller private data | |
279 | * @hw_id: Hardware ID aka. sensor number | |
280 | * @d: Pointer to irq state data | |
281 | * | |
282 | * Return: 0 if threshold was not violated, 1 if it was violated and negative | |
283 | * errno in case of errors | |
284 | */ | |
285 | static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, | |
286 | struct tsens_irq_data *d) | |
287 | { | |
288 | int ret; | |
289 | ||
290 | ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol); | |
291 | if (ret) | |
292 | return ret; | |
293 | ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol); | |
294 | if (ret) | |
295 | return ret; | |
296 | ||
297 | if (priv->feat->crit_int) { | |
298 | ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id], | |
299 | &d->crit_viol); | |
300 | if (ret) | |
301 | return ret; | |
302 | } | |
303 | ||
304 | if (d->up_viol || d->low_viol || d->crit_viol) | |
305 | return 1; | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, | |
311 | const struct tsens_sensor *s, | |
312 | struct tsens_irq_data *d) | |
313 | { | |
314 | int ret; | |
315 | ||
316 | ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear); | |
317 | if (ret) | |
318 | return ret; | |
319 | ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); | |
320 | if (ret) | |
321 | return ret; | |
322 | if (tsens_version(priv) > VER_1_X) { | |
323 | ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); | |
324 | if (ret) | |
325 | return ret; | |
326 | ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); | |
327 | if (ret) | |
328 | return ret; | |
329 | ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id], | |
330 | &d->crit_irq_clear); | |
331 | if (ret) | |
332 | return ret; | |
333 | ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id], | |
334 | &d->crit_irq_mask); | |
335 | if (ret) | |
336 | return ret; | |
337 | ||
338 | d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); | |
339 | } else { | |
340 | /* No mask register on older TSENS */ | |
341 | d->up_irq_mask = 0; | |
342 | d->low_irq_mask = 0; | |
343 | d->crit_irq_clear = 0; | |
344 | d->crit_irq_mask = 0; | |
345 | d->crit_thresh = 0; | |
346 | } | |
347 | ||
348 | d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); | |
349 | d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); | |
350 | ||
351 | dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", | |
352 | hw_id, __func__, | |
353 | (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", | |
354 | d->low_viol, d->up_viol, d->crit_viol, | |
355 | d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear, | |
356 | d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask); | |
357 | dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__, | |
358 | (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", | |
359 | d->low_thresh, d->up_thresh, d->crit_thresh); | |
360 | ||
361 | return 0; | |
362 | } | |
363 | ||
364 | static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) | |
365 | { | |
366 | if (ver > VER_1_X) | |
367 | return mask & (1 << hw_id); | |
368 | ||
369 | /* v1, v0.1 don't have a irq mask register */ | |
370 | return 0; | |
371 | } | |
372 | ||
373 | /** | |
374 | * tsens_critical_irq_thread() - Threaded handler for critical interrupts | |
375 | * @irq: irq number | |
376 | * @data: tsens controller private data | |
377 | * | |
378 | * Check FSM watchdog bark status and clear if needed. | |
379 | * Check all sensors to find ones that violated their critical threshold limits. | |
380 | * Clear and then re-enable the interrupt. | |
381 | * | |
382 | * The level-triggered interrupt might deassert if the temperature returned to | |
383 | * within the threshold limits by the time the handler got scheduled. We | |
384 | * consider the irq to have been handled in that case. | |
385 | * | |
386 | * Return: IRQ_HANDLED | |
387 | */ | |
3ecc8292 | 388 | static irqreturn_t tsens_critical_irq_thread(int irq, void *data) |
a7ff8297 AK |
389 | { |
390 | struct tsens_priv *priv = data; | |
391 | struct tsens_irq_data d; | |
392 | int temp, ret, i; | |
393 | u32 wdog_status, wdog_count; | |
394 | ||
395 | if (priv->feat->has_watchdog) { | |
396 | ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS], | |
397 | &wdog_status); | |
398 | if (ret) | |
399 | return ret; | |
400 | ||
401 | if (wdog_status) { | |
402 | /* Clear WDOG interrupt */ | |
403 | regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1); | |
404 | regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0); | |
405 | ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT], | |
406 | &wdog_count); | |
407 | if (ret) | |
408 | return ret; | |
409 | if (wdog_count) | |
410 | dev_dbg(priv->dev, "%s: watchdog count: %d\n", | |
411 | __func__, wdog_count); | |
412 | ||
413 | /* Fall through to handle critical interrupts if any */ | |
414 | } | |
415 | } | |
416 | ||
417 | for (i = 0; i < priv->num_sensors; i++) { | |
418 | const struct tsens_sensor *s = &priv->sensor[i]; | |
419 | u32 hw_id = s->hw_id; | |
420 | ||
cf969218 | 421 | if (!s->tzd) |
a7ff8297 AK |
422 | continue; |
423 | if (!tsens_threshold_violated(priv, hw_id, &d)) | |
424 | continue; | |
425 | ret = get_temp_tsens_valid(s, &temp); | |
426 | if (ret) { | |
427 | dev_err(priv->dev, "[%u] %s: error reading sensor\n", | |
428 | hw_id, __func__); | |
429 | continue; | |
430 | } | |
431 | ||
432 | tsens_read_irq_state(priv, hw_id, s, &d); | |
433 | if (d.crit_viol && | |
434 | !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) { | |
435 | /* Mask critical interrupts, unused on Linux */ | |
436 | tsens_set_interrupt(priv, hw_id, CRITICAL, false); | |
437 | } | |
438 | } | |
439 | ||
440 | return IRQ_HANDLED; | |
441 | } | |
442 | ||
443 | /** | |
444 | * tsens_irq_thread - Threaded interrupt handler for uplow interrupts | |
445 | * @irq: irq number | |
446 | * @data: tsens controller private data | |
447 | * | |
448 | * Check all sensors to find ones that violated their threshold limits. If the | |
449 | * temperature is still outside the limits, call thermal_zone_device_update() to | |
450 | * update the thresholds, else re-enable the interrupts. | |
451 | * | |
452 | * The level-triggered interrupt might deassert if the temperature returned to | |
453 | * within the threshold limits by the time the handler got scheduled. We | |
454 | * consider the irq to have been handled in that case. | |
455 | * | |
456 | * Return: IRQ_HANDLED | |
457 | */ | |
3ecc8292 | 458 | static irqreturn_t tsens_irq_thread(int irq, void *data) |
a7ff8297 AK |
459 | { |
460 | struct tsens_priv *priv = data; | |
461 | struct tsens_irq_data d; | |
462 | bool enable = true, disable = false; | |
463 | unsigned long flags; | |
464 | int temp, ret, i; | |
465 | ||
466 | for (i = 0; i < priv->num_sensors; i++) { | |
467 | bool trigger = false; | |
468 | const struct tsens_sensor *s = &priv->sensor[i]; | |
469 | u32 hw_id = s->hw_id; | |
470 | ||
cf969218 | 471 | if (!s->tzd) |
a7ff8297 AK |
472 | continue; |
473 | if (!tsens_threshold_violated(priv, hw_id, &d)) | |
474 | continue; | |
475 | ret = get_temp_tsens_valid(s, &temp); | |
476 | if (ret) { | |
477 | dev_err(priv->dev, "[%u] %s: error reading sensor\n", | |
478 | hw_id, __func__); | |
479 | continue; | |
480 | } | |
481 | ||
482 | spin_lock_irqsave(&priv->ul_lock, flags); | |
483 | ||
484 | tsens_read_irq_state(priv, hw_id, s, &d); | |
485 | ||
486 | if (d.up_viol && | |
487 | !masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) { | |
488 | tsens_set_interrupt(priv, hw_id, UPPER, disable); | |
489 | if (d.up_thresh > temp) { | |
490 | dev_dbg(priv->dev, "[%u] %s: re-arm upper\n", | |
491 | hw_id, __func__); | |
492 | tsens_set_interrupt(priv, hw_id, UPPER, enable); | |
493 | } else { | |
494 | trigger = true; | |
495 | /* Keep irq masked */ | |
496 | } | |
497 | } else if (d.low_viol && | |
498 | !masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) { | |
499 | tsens_set_interrupt(priv, hw_id, LOWER, disable); | |
500 | if (d.low_thresh < temp) { | |
501 | dev_dbg(priv->dev, "[%u] %s: re-arm low\n", | |
502 | hw_id, __func__); | |
503 | tsens_set_interrupt(priv, hw_id, LOWER, enable); | |
504 | } else { | |
505 | trigger = true; | |
506 | /* Keep irq masked */ | |
507 | } | |
508 | } | |
509 | ||
510 | spin_unlock_irqrestore(&priv->ul_lock, flags); | |
511 | ||
512 | if (trigger) { | |
513 | dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n", | |
514 | hw_id, __func__, temp); | |
515 | thermal_zone_device_update(s->tzd, | |
516 | THERMAL_EVENT_UNSPECIFIED); | |
517 | } else { | |
518 | dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", | |
519 | hw_id, __func__, temp); | |
520 | } | |
53e2a20e AS |
521 | |
522 | if (tsens_version(priv) < VER_0_1) { | |
523 | /* Constraint: There is only 1 interrupt control register for all | |
524 | * 11 temperature sensor. So monitoring more than 1 sensor based | |
525 | * on interrupts will yield inconsistent result. To overcome this | |
526 | * issue we will monitor only sensor 0 which is the master sensor. | |
527 | */ | |
528 | break; | |
529 | } | |
a7ff8297 AK |
530 | } |
531 | ||
532 | return IRQ_HANDLED; | |
533 | } | |
534 | ||
4360af35 RM |
535 | /** |
536 | * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts | |
537 | * @irq: irq number | |
538 | * @data: tsens controller private data | |
539 | * | |
540 | * Handle the combined interrupt as if it were 2 separate interrupts, so call the | |
541 | * critical handler first and then the up/low one. | |
542 | * | |
543 | * Return: IRQ_HANDLED | |
544 | */ | |
545 | static irqreturn_t tsens_combined_irq_thread(int irq, void *data) | |
546 | { | |
547 | irqreturn_t ret; | |
548 | ||
549 | ret = tsens_critical_irq_thread(irq, data); | |
550 | if (ret != IRQ_HANDLED) | |
551 | return ret; | |
552 | ||
553 | return tsens_irq_thread(irq, data); | |
554 | } | |
555 | ||
ca1b9a9e | 556 | static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high) |
a7ff8297 | 557 | { |
ca1b9a9e | 558 | struct tsens_sensor *s = tz->devdata; |
a7ff8297 AK |
559 | struct tsens_priv *priv = s->priv; |
560 | struct device *dev = priv->dev; | |
561 | struct tsens_irq_data d; | |
562 | unsigned long flags; | |
563 | int high_val, low_val, cl_high, cl_low; | |
564 | u32 hw_id = s->hw_id; | |
565 | ||
53e2a20e AS |
566 | if (tsens_version(priv) < VER_0_1) { |
567 | /* Pre v0.1 IP had a single register for each type of interrupt | |
568 | * and thresholds | |
569 | */ | |
570 | hw_id = 0; | |
571 | } | |
572 | ||
a7ff8297 AK |
573 | dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", |
574 | hw_id, __func__, low, high); | |
575 | ||
f63baced RM |
576 | cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp); |
577 | cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp); | |
a7ff8297 AK |
578 | |
579 | high_val = tsens_mC_to_hw(s, cl_high); | |
580 | low_val = tsens_mC_to_hw(s, cl_low); | |
581 | ||
582 | spin_lock_irqsave(&priv->ul_lock, flags); | |
583 | ||
584 | tsens_read_irq_state(priv, hw_id, s, &d); | |
585 | ||
586 | /* Write the new thresholds and clear the status */ | |
587 | regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val); | |
588 | regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val); | |
589 | tsens_set_interrupt(priv, hw_id, LOWER, true); | |
590 | tsens_set_interrupt(priv, hw_id, UPPER, true); | |
591 | ||
592 | spin_unlock_irqrestore(&priv->ul_lock, flags); | |
593 | ||
594 | dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n", | |
595 | hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high); | |
596 | ||
597 | return 0; | |
598 | } | |
599 | ||
3ecc8292 | 600 | static int tsens_enable_irq(struct tsens_priv *priv) |
a7ff8297 AK |
601 | { |
602 | int ret; | |
603 | int val = tsens_version(priv) > VER_1_X ? 7 : 1; | |
604 | ||
605 | ret = regmap_field_write(priv->rf[INT_EN], val); | |
606 | if (ret < 0) | |
607 | dev_err(priv->dev, "%s: failed to enable interrupts\n", | |
608 | __func__); | |
609 | ||
610 | return ret; | |
611 | } | |
612 | ||
3ecc8292 | 613 | static void tsens_disable_irq(struct tsens_priv *priv) |
a7ff8297 AK |
614 | { |
615 | regmap_field_write(priv->rf[INT_EN], 0); | |
616 | } | |
617 | ||
618 | int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) | |
619 | { | |
620 | struct tsens_priv *priv = s->priv; | |
621 | int hw_id = s->hw_id; | |
622 | u32 temp_idx = LAST_TEMP_0 + hw_id; | |
623 | u32 valid_idx = VALID_0 + hw_id; | |
624 | u32 valid; | |
625 | int ret; | |
626 | ||
53e2a20e | 627 | /* VER_0 doesn't have VALID bit */ |
d012f918 AS |
628 | if (tsens_version(priv) == VER_0) |
629 | goto get_temp; | |
630 | ||
631 | /* Valid bit is 0 for 6 AHB clock cycles. | |
632 | * At 19.2MHz, 1 AHB clock is ~60ns. | |
633 | * We should enter this loop very, very rarely. | |
634 | * Wait 1 us since it's the min of poll_timeout macro. | |
635 | * Old value was 400 ns. | |
636 | */ | |
637 | ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid, | |
638 | valid, 1, 20 * USEC_PER_MSEC); | |
639 | if (ret) | |
640 | return ret; | |
a7ff8297 | 641 | |
d012f918 | 642 | get_temp: |
a7ff8297 AK |
643 | /* Valid bit is set, OK to read the temperature */ |
644 | *temp = tsens_hw_to_mC(s, temp_idx); | |
645 | ||
646 | return 0; | |
647 | } | |
648 | ||
649 | int get_temp_common(const struct tsens_sensor *s, int *temp) | |
650 | { | |
651 | struct tsens_priv *priv = s->priv; | |
652 | int hw_id = s->hw_id; | |
53e2a20e AS |
653 | int last_temp = 0, ret, trdy; |
654 | unsigned long timeout; | |
a7ff8297 | 655 | |
53e2a20e AS |
656 | timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); |
657 | do { | |
658 | if (tsens_version(priv) == VER_0) { | |
659 | ret = regmap_field_read(priv->rf[TRDY], &trdy); | |
660 | if (ret) | |
661 | return ret; | |
662 | if (!trdy) | |
663 | continue; | |
664 | } | |
a7ff8297 | 665 | |
53e2a20e AS |
666 | ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); |
667 | if (ret) | |
668 | return ret; | |
a7ff8297 | 669 | |
53e2a20e AS |
670 | *temp = code_to_degc(last_temp, s) * 1000; |
671 | ||
672 | return 0; | |
673 | } while (time_before(jiffies, timeout)); | |
674 | ||
675 | return -ETIMEDOUT; | |
a7ff8297 AK |
676 | } |
677 | ||
678 | #ifdef CONFIG_DEBUG_FS | |
679 | static int dbg_sensors_show(struct seq_file *s, void *data) | |
680 | { | |
681 | struct platform_device *pdev = s->private; | |
682 | struct tsens_priv *priv = platform_get_drvdata(pdev); | |
683 | int i; | |
684 | ||
685 | seq_printf(s, "max: %2d\nnum: %2d\n\n", | |
686 | priv->feat->max_sensors, priv->num_sensors); | |
687 | ||
688 | seq_puts(s, " id slope offset\n--------------------------\n"); | |
689 | for (i = 0; i < priv->num_sensors; i++) { | |
690 | seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id, | |
691 | priv->sensor[i].slope, priv->sensor[i].offset); | |
692 | } | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
697 | static int dbg_version_show(struct seq_file *s, void *data) | |
698 | { | |
699 | struct platform_device *pdev = s->private; | |
700 | struct tsens_priv *priv = platform_get_drvdata(pdev); | |
701 | u32 maj_ver, min_ver, step_ver; | |
702 | int ret; | |
703 | ||
704 | if (tsens_version(priv) > VER_0_1) { | |
705 | ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver); | |
706 | if (ret) | |
707 | return ret; | |
708 | ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver); | |
709 | if (ret) | |
710 | return ret; | |
711 | ret = regmap_field_read(priv->rf[VER_STEP], &step_ver); | |
712 | if (ret) | |
713 | return ret; | |
714 | seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver); | |
715 | } else { | |
716 | seq_puts(s, "0.1.0\n"); | |
717 | } | |
718 | ||
719 | return 0; | |
720 | } | |
721 | ||
722 | DEFINE_SHOW_ATTRIBUTE(dbg_version); | |
723 | DEFINE_SHOW_ATTRIBUTE(dbg_sensors); | |
724 | ||
725 | static void tsens_debug_init(struct platform_device *pdev) | |
726 | { | |
727 | struct tsens_priv *priv = platform_get_drvdata(pdev); | |
728 | struct dentry *root, *file; | |
729 | ||
730 | root = debugfs_lookup("tsens", NULL); | |
731 | if (!root) | |
732 | priv->debug_root = debugfs_create_dir("tsens", NULL); | |
733 | else | |
734 | priv->debug_root = root; | |
735 | ||
736 | file = debugfs_lookup("version", priv->debug_root); | |
737 | if (!file) | |
738 | debugfs_create_file("version", 0444, priv->debug_root, | |
739 | pdev, &dbg_version_fops); | |
740 | ||
741 | /* A directory for each instance of the TSENS IP */ | |
742 | priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root); | |
743 | debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops); | |
744 | } | |
745 | #else | |
746 | static inline void tsens_debug_init(struct platform_device *pdev) {} | |
747 | #endif | |
748 | ||
749 | static const struct regmap_config tsens_config = { | |
750 | .name = "tm", | |
751 | .reg_bits = 32, | |
752 | .val_bits = 32, | |
753 | .reg_stride = 4, | |
754 | }; | |
755 | ||
756 | static const struct regmap_config tsens_srot_config = { | |
757 | .name = "srot", | |
758 | .reg_bits = 32, | |
759 | .val_bits = 32, | |
760 | .reg_stride = 4, | |
761 | }; | |
762 | ||
763 | int __init init_common(struct tsens_priv *priv) | |
764 | { | |
765 | void __iomem *tm_base, *srot_base; | |
766 | struct device *dev = priv->dev; | |
767 | u32 ver_minor; | |
768 | struct resource *res; | |
769 | u32 enabled; | |
770 | int ret, i, j; | |
771 | struct platform_device *op = of_find_device_by_node(priv->dev->of_node); | |
772 | ||
773 | if (!op) | |
774 | return -EINVAL; | |
775 | ||
776 | if (op->num_resources > 1) { | |
777 | /* DT with separate SROT and TM address space */ | |
778 | priv->tm_offset = 0; | |
779 | res = platform_get_resource(op, IORESOURCE_MEM, 1); | |
780 | srot_base = devm_ioremap_resource(dev, res); | |
781 | if (IS_ERR(srot_base)) { | |
782 | ret = PTR_ERR(srot_base); | |
783 | goto err_put_device; | |
784 | } | |
785 | ||
786 | priv->srot_map = devm_regmap_init_mmio(dev, srot_base, | |
787 | &tsens_srot_config); | |
788 | if (IS_ERR(priv->srot_map)) { | |
789 | ret = PTR_ERR(priv->srot_map); | |
790 | goto err_put_device; | |
791 | } | |
792 | } else { | |
793 | /* old DTs where SROT and TM were in a contiguous 2K block */ | |
794 | priv->tm_offset = 0x1000; | |
795 | } | |
796 | ||
53e2a20e AS |
797 | if (tsens_version(priv) >= VER_0_1) { |
798 | res = platform_get_resource(op, IORESOURCE_MEM, 0); | |
799 | tm_base = devm_ioremap_resource(dev, res); | |
800 | if (IS_ERR(tm_base)) { | |
801 | ret = PTR_ERR(tm_base); | |
802 | goto err_put_device; | |
803 | } | |
804 | ||
805 | priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); | |
806 | } else { /* VER_0 share the same gcc regs using a syscon */ | |
807 | struct device *parent = priv->dev->parent; | |
808 | ||
809 | if (parent) | |
810 | priv->tm_map = syscon_node_to_regmap(parent->of_node); | |
a7ff8297 AK |
811 | } |
812 | ||
53e2a20e AS |
813 | if (IS_ERR_OR_NULL(priv->tm_map)) { |
814 | if (!priv->tm_map) | |
815 | ret = -ENODEV; | |
816 | else | |
817 | ret = PTR_ERR(priv->tm_map); | |
a7ff8297 AK |
818 | goto err_put_device; |
819 | } | |
820 | ||
53e2a20e AS |
821 | /* VER_0 have only tm_map */ |
822 | if (!priv->srot_map) | |
823 | priv->srot_map = priv->tm_map; | |
824 | ||
a7ff8297 AK |
825 | if (tsens_version(priv) > VER_0_1) { |
826 | for (i = VER_MAJOR; i <= VER_STEP; i++) { | |
827 | priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, | |
828 | priv->fields[i]); | |
f4136863 GZ |
829 | if (IS_ERR(priv->rf[i])) { |
830 | ret = PTR_ERR(priv->rf[i]); | |
831 | goto err_put_device; | |
832 | } | |
a7ff8297 AK |
833 | } |
834 | ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); | |
835 | if (ret) | |
836 | goto err_put_device; | |
837 | } | |
838 | ||
839 | priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, | |
840 | priv->fields[TSENS_EN]); | |
841 | if (IS_ERR(priv->rf[TSENS_EN])) { | |
842 | ret = PTR_ERR(priv->rf[TSENS_EN]); | |
843 | goto err_put_device; | |
844 | } | |
53e2a20e AS |
845 | /* in VER_0 TSENS need to be explicitly enabled */ |
846 | if (tsens_version(priv) == VER_0) | |
847 | regmap_field_write(priv->rf[TSENS_EN], 1); | |
848 | ||
a7ff8297 AK |
849 | ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); |
850 | if (ret) | |
851 | goto err_put_device; | |
852 | if (!enabled) { | |
853 | dev_err(dev, "%s: device not enabled\n", __func__); | |
854 | ret = -ENODEV; | |
855 | goto err_put_device; | |
856 | } | |
857 | ||
858 | priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, | |
859 | priv->fields[SENSOR_EN]); | |
860 | if (IS_ERR(priv->rf[SENSOR_EN])) { | |
861 | ret = PTR_ERR(priv->rf[SENSOR_EN]); | |
862 | goto err_put_device; | |
863 | } | |
864 | priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map, | |
865 | priv->fields[INT_EN]); | |
866 | if (IS_ERR(priv->rf[INT_EN])) { | |
867 | ret = PTR_ERR(priv->rf[INT_EN]); | |
868 | goto err_put_device; | |
869 | } | |
870 | ||
53e2a20e AS |
871 | priv->rf[TSENS_SW_RST] = |
872 | devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]); | |
873 | if (IS_ERR(priv->rf[TSENS_SW_RST])) { | |
874 | ret = PTR_ERR(priv->rf[TSENS_SW_RST]); | |
875 | goto err_put_device; | |
876 | } | |
877 | ||
878 | priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]); | |
879 | if (IS_ERR(priv->rf[TRDY])) { | |
880 | ret = PTR_ERR(priv->rf[TRDY]); | |
881 | goto err_put_device; | |
882 | } | |
883 | ||
a7ff8297 AK |
884 | /* This loop might need changes if enum regfield_ids is reordered */ |
885 | for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { | |
886 | for (i = 0; i < priv->feat->max_sensors; i++) { | |
887 | int idx = j + i; | |
888 | ||
889 | priv->rf[idx] = devm_regmap_field_alloc(dev, | |
890 | priv->tm_map, | |
891 | priv->fields[idx]); | |
892 | if (IS_ERR(priv->rf[idx])) { | |
893 | ret = PTR_ERR(priv->rf[idx]); | |
894 | goto err_put_device; | |
895 | } | |
896 | } | |
897 | } | |
898 | ||
53e2a20e | 899 | if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) { |
a7ff8297 AK |
900 | /* Loop might need changes if enum regfield_ids is reordered */ |
901 | for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { | |
902 | for (i = 0; i < priv->feat->max_sensors; i++) { | |
903 | int idx = j + i; | |
904 | ||
905 | priv->rf[idx] = | |
906 | devm_regmap_field_alloc(dev, | |
907 | priv->tm_map, | |
908 | priv->fields[idx]); | |
909 | if (IS_ERR(priv->rf[idx])) { | |
910 | ret = PTR_ERR(priv->rf[idx]); | |
911 | goto err_put_device; | |
912 | } | |
913 | } | |
914 | } | |
915 | } | |
916 | ||
917 | if (tsens_version(priv) > VER_1_X && ver_minor > 2) { | |
918 | /* Watchdog is present only on v2.3+ */ | |
919 | priv->feat->has_watchdog = 1; | |
920 | for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) { | |
921 | priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map, | |
922 | priv->fields[i]); | |
923 | if (IS_ERR(priv->rf[i])) { | |
924 | ret = PTR_ERR(priv->rf[i]); | |
925 | goto err_put_device; | |
926 | } | |
927 | } | |
928 | /* | |
929 | * Watchdog is already enabled, unmask the bark. | |
930 | * Disable cycle completion monitoring | |
931 | */ | |
932 | regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); | |
933 | regmap_field_write(priv->rf[CC_MON_MASK], 1); | |
934 | } | |
935 | ||
936 | spin_lock_init(&priv->ul_lock); | |
53e2a20e AS |
937 | |
938 | /* VER_0 interrupt doesn't need to be enabled */ | |
939 | if (tsens_version(priv) >= VER_0_1) | |
940 | tsens_enable_irq(priv); | |
941 | ||
a7ff8297 AK |
942 | err_put_device: |
943 | put_device(&op->dev); | |
944 | return ret; | |
945 | } | |
946 | ||
ca1b9a9e | 947 | static int tsens_get_temp(struct thermal_zone_device *tz, int *temp) |
9066073c | 948 | { |
ca1b9a9e | 949 | struct tsens_sensor *s = tz->devdata; |
69b628ac | 950 | struct tsens_priv *priv = s->priv; |
9066073c | 951 | |
8b71bce4 | 952 | return priv->ops->get_temp(s, temp); |
9066073c RN |
953 | } |
954 | ||
5b97469a | 955 | static int __maybe_unused tsens_suspend(struct device *dev) |
9066073c | 956 | { |
69b628ac | 957 | struct tsens_priv *priv = dev_get_drvdata(dev); |
9066073c | 958 | |
69b628ac AK |
959 | if (priv->ops && priv->ops->suspend) |
960 | return priv->ops->suspend(priv); | |
9066073c RN |
961 | |
962 | return 0; | |
963 | } | |
964 | ||
5b97469a | 965 | static int __maybe_unused tsens_resume(struct device *dev) |
9066073c | 966 | { |
69b628ac | 967 | struct tsens_priv *priv = dev_get_drvdata(dev); |
9066073c | 968 | |
69b628ac AK |
969 | if (priv->ops && priv->ops->resume) |
970 | return priv->ops->resume(priv); | |
9066073c RN |
971 | |
972 | return 0; | |
973 | } | |
974 | ||
975 | static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); | |
976 | ||
977 | static const struct of_device_id tsens_table[] = { | |
978 | { | |
6b3aeafb AS |
979 | .compatible = "qcom,ipq8064-tsens", |
980 | .data = &data_8960, | |
6840455d RM |
981 | }, { |
982 | .compatible = "qcom,ipq8074-tsens", | |
983 | .data = &data_ipq8074, | |
6b3aeafb | 984 | }, { |
a2149ab8 KD |
985 | .compatible = "qcom,mdm9607-tsens", |
986 | .data = &data_9607, | |
987 | }, { | |
9066073c | 988 | .compatible = "qcom,msm8916-tsens", |
840a5bd3 | 989 | .data = &data_8916, |
332bc8eb SG |
990 | }, { |
991 | .compatible = "qcom,msm8939-tsens", | |
992 | .data = &data_8939, | |
2caf7396 DB |
993 | }, { |
994 | .compatible = "qcom,msm8960-tsens", | |
995 | .data = &data_8960, | |
9066073c RN |
996 | }, { |
997 | .compatible = "qcom,msm8974-tsens", | |
5e6703bd | 998 | .data = &data_8974, |
0e580290 ADR |
999 | }, { |
1000 | .compatible = "qcom,msm8976-tsens", | |
1001 | .data = &data_8976, | |
d059c739 RN |
1002 | }, { |
1003 | .compatible = "qcom,msm8996-tsens", | |
1004 | .data = &data_8996, | |
e8c24c6f AK |
1005 | }, { |
1006 | .compatible = "qcom,tsens-v1", | |
1007 | .data = &data_tsens_v1, | |
191dc74b AK |
1008 | }, { |
1009 | .compatible = "qcom,tsens-v2", | |
1010 | .data = &data_tsens_v2, | |
9066073c RN |
1011 | }, |
1012 | {} | |
1013 | }; | |
1014 | MODULE_DEVICE_TABLE(of, tsens_table); | |
1015 | ||
ca1b9a9e | 1016 | static const struct thermal_zone_device_ops tsens_of_ops = { |
9066073c | 1017 | .get_temp = tsens_get_temp, |
634e11d5 | 1018 | .set_trips = tsens_set_trips, |
9066073c RN |
1019 | }; |
1020 | ||
79125e03 AK |
1021 | static int tsens_register_irq(struct tsens_priv *priv, char *irqname, |
1022 | irq_handler_t thread_fn) | |
1023 | { | |
1024 | struct platform_device *pdev; | |
1025 | int ret, irq; | |
1026 | ||
1027 | pdev = of_find_device_by_node(priv->dev->of_node); | |
1028 | if (!pdev) | |
1029 | return -ENODEV; | |
1030 | ||
1031 | irq = platform_get_irq_byname(pdev, irqname); | |
1032 | if (irq < 0) { | |
1033 | ret = irq; | |
1034 | /* For old DTs with no IRQ defined */ | |
1035 | if (irq == -ENXIO) | |
1036 | ret = 0; | |
1037 | } else { | |
53e2a20e AS |
1038 | /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */ |
1039 | if (tsens_version(priv) == VER_0) | |
1040 | ret = devm_request_threaded_irq(&pdev->dev, irq, | |
1041 | thread_fn, NULL, | |
1042 | IRQF_TRIGGER_RISING, | |
1043 | dev_name(&pdev->dev), | |
1044 | priv); | |
1045 | else | |
1046 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | |
1047 | thread_fn, IRQF_ONESHOT, | |
1048 | dev_name(&pdev->dev), | |
1049 | priv); | |
1050 | ||
79125e03 AK |
1051 | if (ret) |
1052 | dev_err(&pdev->dev, "%s: failed to get irq\n", | |
1053 | __func__); | |
1054 | else | |
1055 | enable_irq_wake(irq); | |
1056 | } | |
1057 | ||
1058 | put_device(&pdev->dev); | |
1059 | return ret; | |
1060 | } | |
1061 | ||
69b628ac | 1062 | static int tsens_register(struct tsens_priv *priv) |
9066073c | 1063 | { |
79125e03 | 1064 | int i, ret; |
9066073c | 1065 | struct thermal_zone_device *tzd; |
9066073c | 1066 | |
69b628ac AK |
1067 | for (i = 0; i < priv->num_sensors; i++) { |
1068 | priv->sensor[i].priv = priv; | |
ca1b9a9e DL |
1069 | tzd = devm_thermal_of_zone_register(priv->dev, priv->sensor[i].hw_id, |
1070 | &priv->sensor[i], | |
1071 | &tsens_of_ops); | |
9066073c RN |
1072 | if (IS_ERR(tzd)) |
1073 | continue; | |
69b628ac AK |
1074 | priv->sensor[i].tzd = tzd; |
1075 | if (priv->ops->enable) | |
1076 | priv->ops->enable(priv, i); | |
8556e19d DB |
1077 | |
1078 | if (devm_thermal_add_hwmon_sysfs(tzd)) | |
1079 | dev_warn(priv->dev, | |
1080 | "Failed to add hwmon sysfs attributes\n"); | |
9066073c | 1081 | } |
634e11d5 | 1082 | |
53e2a20e AS |
1083 | /* VER_0 require to set MIN and MAX THRESH |
1084 | * These 2 regs are set using the: | |
1085 | * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C | |
1086 | * - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C | |
1087 | */ | |
1088 | if (tsens_version(priv) < VER_0_1) { | |
1089 | regmap_field_write(priv->rf[CRIT_THRESH_0], | |
1090 | tsens_mC_to_hw(priv->sensor, 120000)); | |
1091 | ||
1092 | regmap_field_write(priv->rf[CRIT_THRESH_1], | |
1093 | tsens_mC_to_hw(priv->sensor, 0)); | |
1094 | } | |
1095 | ||
4360af35 RM |
1096 | if (priv->feat->combo_int) { |
1097 | ret = tsens_register_irq(priv, "combined", | |
1098 | tsens_combined_irq_thread); | |
1099 | } else { | |
1100 | ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); | |
1101 | if (ret < 0) | |
1102 | return ret; | |
634e11d5 | 1103 | |
4360af35 RM |
1104 | if (priv->feat->crit_int) |
1105 | ret = tsens_register_irq(priv, "critical", | |
1106 | tsens_critical_irq_thread); | |
1107 | } | |
634e11d5 | 1108 | |
634e11d5 | 1109 | return ret; |
9066073c RN |
1110 | } |
1111 | ||
1112 | static int tsens_probe(struct platform_device *pdev) | |
1113 | { | |
1114 | int ret, i; | |
1115 | struct device *dev; | |
1116 | struct device_node *np; | |
69b628ac | 1117 | struct tsens_priv *priv; |
3c040ce0 | 1118 | const struct tsens_plat_data *data; |
9066073c | 1119 | const struct of_device_id *id; |
6d7c70d1 | 1120 | u32 num_sensors; |
9066073c RN |
1121 | |
1122 | if (pdev->dev.of_node) | |
1123 | dev = &pdev->dev; | |
1124 | else | |
1125 | dev = pdev->dev.parent; | |
1126 | ||
1127 | np = dev->of_node; | |
1128 | ||
1129 | id = of_match_node(tsens_table, np); | |
20d4fd84 RN |
1130 | if (id) |
1131 | data = id->data; | |
1132 | else | |
1133 | data = &data_8960; | |
9066073c | 1134 | |
6d7c70d1 BA |
1135 | num_sensors = data->num_sensors; |
1136 | ||
1137 | if (np) | |
1138 | of_property_read_u32(np, "#qcom,sensors", &num_sensors); | |
1139 | ||
1140 | if (num_sensors <= 0) { | |
3795ad5e | 1141 | dev_err(dev, "%s: invalid number of sensors\n", __func__); |
9066073c RN |
1142 | return -EINVAL; |
1143 | } | |
1144 | ||
69b628ac AK |
1145 | priv = devm_kzalloc(dev, |
1146 | struct_size(priv, sensor, num_sensors), | |
0ed2dd03 | 1147 | GFP_KERNEL); |
69b628ac | 1148 | if (!priv) |
9066073c RN |
1149 | return -ENOMEM; |
1150 | ||
69b628ac AK |
1151 | priv->dev = dev; |
1152 | priv->num_sensors = num_sensors; | |
1153 | priv->ops = data->ops; | |
1154 | for (i = 0; i < priv->num_sensors; i++) { | |
9066073c | 1155 | if (data->hw_ids) |
69b628ac | 1156 | priv->sensor[i].hw_id = data->hw_ids[i]; |
9066073c | 1157 | else |
69b628ac | 1158 | priv->sensor[i].hw_id = i; |
9066073c | 1159 | } |
c1997054 AK |
1160 | priv->feat = data->feat; |
1161 | priv->fields = data->fields; | |
9066073c | 1162 | |
0e9c0bc7 AK |
1163 | platform_set_drvdata(pdev, priv); |
1164 | ||
69b628ac | 1165 | if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) |
9066073c RN |
1166 | return -EINVAL; |
1167 | ||
69b628ac | 1168 | ret = priv->ops->init(priv); |
9066073c | 1169 | if (ret < 0) { |
3795ad5e | 1170 | dev_err(dev, "%s: init failed\n", __func__); |
9066073c RN |
1171 | return ret; |
1172 | } | |
1173 | ||
69b628ac AK |
1174 | if (priv->ops->calibrate) { |
1175 | ret = priv->ops->calibrate(priv); | |
9066073c | 1176 | if (ret < 0) { |
fc7d18cf | 1177 | if (ret != -EPROBE_DEFER) |
3795ad5e | 1178 | dev_err(dev, "%s: calibration failed\n", __func__); |
9066073c RN |
1179 | return ret; |
1180 | } | |
1181 | } | |
1182 | ||
de48d876 CM |
1183 | ret = tsens_register(priv); |
1184 | if (!ret) | |
1185 | tsens_debug_init(pdev); | |
1186 | ||
1187 | return ret; | |
9066073c RN |
1188 | } |
1189 | ||
1190 | static int tsens_remove(struct platform_device *pdev) | |
1191 | { | |
69b628ac | 1192 | struct tsens_priv *priv = platform_get_drvdata(pdev); |
9066073c | 1193 | |
7c938f48 | 1194 | debugfs_remove_recursive(priv->debug_root); |
634e11d5 | 1195 | tsens_disable_irq(priv); |
69b628ac AK |
1196 | if (priv->ops->disable) |
1197 | priv->ops->disable(priv); | |
9066073c RN |
1198 | |
1199 | return 0; | |
1200 | } | |
1201 | ||
1202 | static struct platform_driver tsens_driver = { | |
1203 | .probe = tsens_probe, | |
1204 | .remove = tsens_remove, | |
1205 | .driver = { | |
1206 | .name = "qcom-tsens", | |
1207 | .pm = &tsens_pm_ops, | |
1208 | .of_match_table = tsens_table, | |
1209 | }, | |
1210 | }; | |
1211 | module_platform_driver(tsens_driver); | |
1212 | ||
1213 | MODULE_LICENSE("GPL v2"); | |
1214 | MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); | |
1215 | MODULE_ALIAS("platform:qcom-tsens"); |