OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From 3302e1e1a3cfa4e67fda2a61d6f0c42205d40932 Mon Sep 17 00:00:00 2001 |
2 | From: Rajith Cherian <rajith@codeaurora.org> |
||
3 | Date: Tue, 14 Feb 2017 18:30:43 +0530 |
||
4 | Subject: [PATCH] ipq8064: tsens: Base tsens driver for IPQ8064 |
||
5 | |||
6 | Add TSENS driver template to support IPQ8064. |
||
7 | This is a base file copied from tsens-8960.c |
||
8 | |||
9 | Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7 |
||
10 | Signed-off-by: Rajith Cherian <rajith@codeaurora.org> |
||
11 | |||
12 | ipq8064: tsens: TSENS driver support for IPQ8064 |
||
13 | |||
14 | Support for IPQ8064 tsens driver. The driver works |
||
15 | with the thermal framework. The driver overrides the |
||
16 | following fucntionalities: |
||
17 | |||
18 | 1. Get current temperature. |
||
19 | 2. Get/Set trip temperatures. |
||
20 | 3. Enabled/Disable trip points. |
||
21 | 4. ISR for threshold generated interrupt. |
||
22 | 5. Notify userspace when trip points are hit. |
||
23 | |||
24 | Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918 |
||
25 | Signed-off-by: Rajith Cherian <rajith@codeaurora.org> |
||
26 | --- |
||
27 | .../devicetree/bindings/thermal/qcom-tsens.txt | 1 + |
||
28 | drivers/thermal/qcom/Makefile | 3 +- |
||
29 | drivers/thermal/qcom/tsens-ipq8064.c | 551 +++++++++++++++++++++ |
||
30 | drivers/thermal/qcom/tsens.c | 3 + |
||
31 | drivers/thermal/qcom/tsens.h | 2 +- |
||
32 | 5 files changed, 558 insertions(+), 2 deletions(-) |
||
33 | create mode 100644 drivers/thermal/qcom/tsens-ipq8064.c |
||
34 | |||
35 | --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt |
||
36 | +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt |
||
37 | @@ -5,6 +5,7 @@ Required properties: |
||
38 | - "qcom,msm8916-tsens" : For 8916 Family of SoCs |
||
39 | - "qcom,msm8974-tsens" : For 8974 Family of SoCs |
||
40 | - "qcom,msm8996-tsens" : For 8996 Family of SoCs |
||
41 | + - "qcom,ipq8064-tsens" : For IPQ8064 |
||
42 | |||
43 | - reg: Address range of the thermal registers |
||
44 | - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. |
||
45 | --- a/drivers/thermal/qcom/Makefile |
||
46 | +++ b/drivers/thermal/qcom/Makefile |
||
47 | @@ -1,2 +1,3 @@ |
||
48 | obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o |
||
49 | -qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o |
||
50 | +qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o \ |
||
51 | + tsens-ipq8064.o |
||
52 | --- /dev/null |
||
53 | +++ b/drivers/thermal/qcom/tsens-ipq8064.c |
||
54 | @@ -0,0 +1,551 @@ |
||
55 | +/* |
||
56 | + * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
||
57 | + * |
||
58 | + * This program is free software; you can redistribute it and/or modify |
||
59 | + * it under the terms of the GNU General Public License version 2 and |
||
60 | + * only version 2 as published by the Free Software Foundation. |
||
61 | + * |
||
62 | + * This program is distributed in the hope that it will be useful, |
||
63 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
64 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
65 | + * GNU General Public License for more details. |
||
66 | + * |
||
67 | + */ |
||
68 | + |
||
69 | +#include <linux/platform_device.h> |
||
70 | +#include <linux/delay.h> |
||
71 | +#include <linux/bitops.h> |
||
72 | +#include <linux/regmap.h> |
||
73 | +#include <linux/thermal.h> |
||
74 | +#include <linux/nvmem-consumer.h> |
||
75 | +#include <linux/io.h> |
||
76 | +#include <linux/interrupt.h> |
||
77 | +#include "tsens.h" |
||
78 | + |
||
79 | +#define CAL_MDEGC 30000 |
||
80 | + |
||
81 | +#define CONFIG_ADDR 0x3640 |
||
82 | +/* CONFIG_ADDR bitmasks */ |
||
83 | +#define CONFIG 0x9b |
||
84 | +#define CONFIG_MASK 0xf |
||
85 | +#define CONFIG_SHIFT 0 |
||
86 | + |
||
87 | +#define STATUS_CNTL_8064 0x3660 |
||
88 | +#define CNTL_ADDR 0x3620 |
||
89 | +/* CNTL_ADDR bitmasks */ |
||
90 | +#define EN BIT(0) |
||
91 | +#define SW_RST BIT(1) |
||
92 | +#define SENSOR0_EN BIT(3) |
||
93 | +#define SLP_CLK_ENA BIT(26) |
||
94 | +#define MEASURE_PERIOD 1 |
||
95 | +#define SENSOR0_SHIFT 3 |
||
96 | + |
||
97 | +/* INT_STATUS_ADDR bitmasks */ |
||
98 | +#define MIN_STATUS_MASK BIT(0) |
||
99 | +#define LOWER_STATUS_CLR BIT(1) |
||
100 | +#define UPPER_STATUS_CLR BIT(2) |
||
101 | +#define MAX_STATUS_MASK BIT(3) |
||
102 | + |
||
103 | +#define THRESHOLD_ADDR 0x3624 |
||
104 | +/* THRESHOLD_ADDR bitmasks */ |
||
105 | +#define THRESHOLD_MAX_CODE 0x20000 |
||
106 | +#define THRESHOLD_MIN_CODE 0 |
||
107 | +#define THRESHOLD_MAX_LIMIT_SHIFT 24 |
||
108 | +#define THRESHOLD_MIN_LIMIT_SHIFT 16 |
||
109 | +#define THRESHOLD_UPPER_LIMIT_SHIFT 8 |
||
110 | +#define THRESHOLD_LOWER_LIMIT_SHIFT 0 |
||
111 | +#define THRESHOLD_MAX_LIMIT_MASK (THRESHOLD_MAX_CODE << \ |
||
112 | + THRESHOLD_MAX_LIMIT_SHIFT) |
||
113 | +#define THRESHOLD_MIN_LIMIT_MASK (THRESHOLD_MAX_CODE << \ |
||
114 | + THRESHOLD_MIN_LIMIT_SHIFT) |
||
115 | +#define THRESHOLD_UPPER_LIMIT_MASK (THRESHOLD_MAX_CODE << \ |
||
116 | + THRESHOLD_UPPER_LIMIT_SHIFT) |
||
117 | +#define THRESHOLD_LOWER_LIMIT_MASK (THRESHOLD_MAX_CODE << \ |
||
118 | + THRESHOLD_LOWER_LIMIT_SHIFT) |
||
119 | + |
||
120 | +/* Initial temperature threshold values */ |
||
121 | +#define LOWER_LIMIT_TH 0x9d /* 95C */ |
||
122 | +#define UPPER_LIMIT_TH 0xa6 /* 105C */ |
||
123 | +#define MIN_LIMIT_TH 0x0 |
||
124 | +#define MAX_LIMIT_TH 0xff |
||
125 | + |
||
126 | +#define S0_STATUS_ADDR 0x3628 |
||
127 | +#define STATUS_ADDR_OFFSET 2 |
||
128 | +#define SENSOR_STATUS_SIZE 4 |
||
129 | +#define INT_STATUS_ADDR 0x363c |
||
130 | +#define TRDY_MASK BIT(7) |
||
131 | +#define TIMEOUT_US 100 |
||
132 | + |
||
133 | +#define TSENS_EN BIT(0) |
||
134 | +#define TSENS_SW_RST BIT(1) |
||
135 | +#define TSENS_ADC_CLK_SEL BIT(2) |
||
136 | +#define SENSOR0_EN BIT(3) |
||
137 | +#define SENSOR1_EN BIT(4) |
||
138 | +#define SENSOR2_EN BIT(5) |
||
139 | +#define SENSOR3_EN BIT(6) |
||
140 | +#define SENSOR4_EN BIT(7) |
||
141 | +#define SENSORS_EN (SENSOR0_EN | SENSOR1_EN | \ |
||
142 | + SENSOR2_EN | SENSOR3_EN | SENSOR4_EN) |
||
143 | +#define TSENS_8064_SENSOR5_EN BIT(8) |
||
144 | +#define TSENS_8064_SENSOR6_EN BIT(9) |
||
145 | +#define TSENS_8064_SENSOR7_EN BIT(10) |
||
146 | +#define TSENS_8064_SENSOR8_EN BIT(11) |
||
147 | +#define TSENS_8064_SENSOR9_EN BIT(12) |
||
148 | +#define TSENS_8064_SENSOR10_EN BIT(13) |
||
149 | +#define TSENS_8064_SENSORS_EN (SENSORS_EN | \ |
||
150 | + TSENS_8064_SENSOR5_EN | \ |
||
151 | + TSENS_8064_SENSOR6_EN | \ |
||
152 | + TSENS_8064_SENSOR7_EN | \ |
||
153 | + TSENS_8064_SENSOR8_EN | \ |
||
154 | + TSENS_8064_SENSOR9_EN | \ |
||
155 | + TSENS_8064_SENSOR10_EN) |
||
156 | + |
||
157 | +#define TSENS_8064_SEQ_SENSORS 5 |
||
158 | +#define TSENS_8064_S4_S5_OFFSET 40 |
||
159 | +#define TSENS_FACTOR 1 |
||
160 | + |
||
161 | +/* Trips: from very hot to very cold */ |
||
162 | +enum tsens_trip_type { |
||
163 | + TSENS_TRIP_STAGE3 = 0, |
||
164 | + TSENS_TRIP_STAGE2, |
||
165 | + TSENS_TRIP_STAGE1, |
||
166 | + TSENS_TRIP_STAGE0, |
||
167 | + TSENS_TRIP_NUM, |
||
168 | +}; |
||
169 | + |
||
170 | +u32 tsens_8064_slope[] = { |
||
171 | + 1176, 1176, 1154, 1176, |
||
172 | + 1111, 1132, 1132, 1199, |
||
173 | + 1132, 1199, 1132 |
||
174 | + }; |
||
175 | + |
||
176 | +/* Temperature on y axis and ADC-code on x-axis */ |
||
177 | +static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s) |
||
178 | +{ |
||
179 | + int degcbeforefactor, degc; |
||
180 | + |
||
181 | + degcbeforefactor = (adc_code * s->slope) + s->offset; |
||
182 | + |
||
183 | + if (degcbeforefactor == 0) |
||
184 | + degc = degcbeforefactor; |
||
185 | + else if (degcbeforefactor > 0) |
||
186 | + degc = (degcbeforefactor + TSENS_FACTOR/2) |
||
187 | + / TSENS_FACTOR; |
||
188 | + else |
||
189 | + degc = (degcbeforefactor - TSENS_FACTOR/2) |
||
190 | + / TSENS_FACTOR; |
||
191 | + |
||
192 | + return degc; |
||
193 | +} |
||
194 | + |
||
195 | +static int degC_to_code(int degC, const struct tsens_sensor *s) |
||
196 | +{ |
||
197 | + int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2)) |
||
198 | + / s->slope; |
||
199 | + |
||
200 | + if (code > THRESHOLD_MAX_CODE) |
||
201 | + code = THRESHOLD_MAX_CODE; |
||
202 | + else if (code < THRESHOLD_MIN_CODE) |
||
203 | + code = THRESHOLD_MIN_CODE; |
||
204 | + return code; |
||
205 | +} |
||
206 | + |
||
207 | +static int suspend_ipq8064(struct tsens_device *tmdev) |
||
208 | +{ |
||
209 | + int ret; |
||
210 | + unsigned int mask; |
||
211 | + struct regmap *map = tmdev->map; |
||
212 | + |
||
213 | + ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); |
||
214 | + if (ret) |
||
215 | + return ret; |
||
216 | + |
||
217 | + ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); |
||
218 | + if (ret) |
||
219 | + return ret; |
||
220 | + |
||
221 | + mask = SLP_CLK_ENA | EN; |
||
222 | + |
||
223 | + ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); |
||
224 | + if (ret) |
||
225 | + return ret; |
||
226 | + |
||
227 | + return 0; |
||
228 | +} |
||
229 | + |
||
230 | +static int resume_ipq8064(struct tsens_device *tmdev) |
||
231 | +{ |
||
232 | + int ret; |
||
233 | + struct regmap *map = tmdev->map; |
||
234 | + |
||
235 | + ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); |
||
236 | + if (ret) |
||
237 | + return ret; |
||
238 | + |
||
239 | + ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); |
||
240 | + if (ret) |
||
241 | + return ret; |
||
242 | + |
||
243 | + ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); |
||
244 | + if (ret) |
||
245 | + return ret; |
||
246 | + |
||
247 | + ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); |
||
248 | + if (ret) |
||
249 | + return ret; |
||
250 | + |
||
251 | + return 0; |
||
252 | +} |
||
253 | + |
||
254 | +static void notify_uspace_tsens_fn(struct work_struct *work) |
||
255 | +{ |
||
256 | + struct tsens_sensor *s = container_of(work, struct tsens_sensor, |
||
257 | + notify_work); |
||
258 | + |
||
259 | + sysfs_notify(&s->tzd->device.kobj, NULL, "type"); |
||
260 | +} |
||
261 | + |
||
262 | +static void tsens_scheduler_fn(struct work_struct *work) |
||
263 | +{ |
||
264 | + struct tsens_device *tmdev = container_of(work, struct tsens_device, |
||
265 | + tsens_work); |
||
266 | + unsigned int threshold, threshold_low, code, reg, sensor, mask; |
||
267 | + unsigned int sensor_addr; |
||
268 | + bool upper_th_x, lower_th_x; |
||
269 | + int adc_code, ret; |
||
270 | + |
||
271 | + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); |
||
272 | + if (ret) |
||
273 | + return; |
||
274 | + reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR; |
||
275 | + ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg); |
||
276 | + if (ret) |
||
277 | + return; |
||
278 | + |
||
279 | + mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR); |
||
280 | + ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold); |
||
281 | + if (ret) |
||
282 | + return; |
||
283 | + threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) |
||
284 | + >> THRESHOLD_LOWER_LIMIT_SHIFT; |
||
285 | + threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) |
||
286 | + >> THRESHOLD_UPPER_LIMIT_SHIFT; |
||
287 | + |
||
288 | + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); |
||
289 | + if (ret) |
||
290 | + return; |
||
291 | + |
||
292 | + ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor); |
||
293 | + if (ret) |
||
294 | + return; |
||
295 | + sensor &= (uint32_t) TSENS_8064_SENSORS_EN; |
||
296 | + sensor >>= SENSOR0_SHIFT; |
||
297 | + |
||
298 | + /* Constraint: There is only 1 interrupt control register for all |
||
299 | + * 11 temperature sensor. So monitoring more than 1 sensor based |
||
300 | + * on interrupts will yield inconsistent result. To overcome this |
||
301 | + * issue we will monitor only sensor 0 which is the master sensor. |
||
302 | + */ |
||
303 | + |
||
304 | + /* Skip if the sensor is disabled */ |
||
305 | + if (sensor & 1) { |
||
306 | + ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code); |
||
307 | + if (ret) |
||
308 | + return; |
||
309 | + upper_th_x = code >= threshold; |
||
310 | + lower_th_x = code <= threshold_low; |
||
311 | + if (upper_th_x) |
||
312 | + mask |= UPPER_STATUS_CLR; |
||
313 | + if (lower_th_x) |
||
314 | + mask |= LOWER_STATUS_CLR; |
||
315 | + if (upper_th_x || lower_th_x) { |
||
316 | + /* Notify user space */ |
||
317 | + schedule_work(&tmdev->sensor[0].notify_work); |
||
318 | + regmap_read(tmdev->map, sensor_addr, &adc_code); |
||
319 | + pr_debug("Trigger (%d degrees) for sensor %d\n", |
||
320 | + code_to_degC(adc_code, &tmdev->sensor[0]), 0); |
||
321 | + } |
||
322 | + } |
||
323 | + regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask); |
||
324 | + |
||
325 | + /* force memory to sync */ |
||
326 | + mb(); |
||
327 | +} |
||
328 | + |
||
329 | +static irqreturn_t tsens_isr(int irq, void *data) |
||
330 | +{ |
||
331 | + struct tsens_device *tmdev = data; |
||
332 | + |
||
333 | + schedule_work(&tmdev->tsens_work); |
||
334 | + return IRQ_HANDLED; |
||
335 | +} |
||
336 | + |
||
337 | +static void hw_init(struct tsens_device *tmdev) |
||
338 | +{ |
||
339 | + int ret; |
||
340 | + unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0; |
||
341 | + unsigned int reg_status_cntl = 0; |
||
342 | + |
||
343 | + regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); |
||
344 | + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST); |
||
345 | + |
||
346 | + reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) |
||
347 | + | (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT); |
||
348 | + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); |
||
349 | + regmap_read(tmdev->map, STATUS_CNTL_8064, ®_status_cntl); |
||
350 | + reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR |
||
351 | + | MIN_STATUS_MASK | MAX_STATUS_MASK; |
||
352 | + regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl); |
||
353 | + reg_cntl |= TSENS_EN; |
||
354 | + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); |
||
355 | + |
||
356 | + regmap_read(tmdev->map, CONFIG_ADDR, ®_cfg); |
||
357 | + reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT); |
||
358 | + regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg); |
||
359 | + |
||
360 | + reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT) |
||
361 | + | (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT) |
||
362 | + | (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) |
||
363 | + | (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT); |
||
364 | + |
||
365 | + regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr); |
||
366 | + |
||
367 | + ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr, |
||
368 | + IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev); |
||
369 | + if (ret < 0) { |
||
370 | + pr_err("%s: request_irq FAIL: %d\n", __func__, ret); |
||
371 | + return; |
||
372 | + } |
||
373 | + |
||
374 | + INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn); |
||
375 | +} |
||
376 | + |
||
377 | +static int init_ipq8064(struct tsens_device *tmdev) |
||
378 | +{ |
||
379 | + int ret, i; |
||
380 | + u32 reg_cntl, offset = 0; |
||
381 | + |
||
382 | + init_common(tmdev); |
||
383 | + if (!tmdev->map) |
||
384 | + return -ENODEV; |
||
385 | + |
||
386 | + /* |
||
387 | + * The status registers for each sensor are discontiguous |
||
388 | + * because some SoCs have 5 sensors while others have more |
||
389 | + * but the control registers stay in the same place, i.e |
||
390 | + * directly after the first 5 status registers. |
||
391 | + */ |
||
392 | + for (i = 0; i < tmdev->num_sensors; i++) { |
||
393 | + if (i >= TSENS_8064_SEQ_SENSORS) |
||
394 | + offset = TSENS_8064_S4_S5_OFFSET; |
||
395 | + |
||
396 | + tmdev->sensor[i].status = S0_STATUS_ADDR + offset |
||
397 | + + (i << STATUS_ADDR_OFFSET); |
||
398 | + tmdev->sensor[i].slope = tsens_8064_slope[i]; |
||
399 | + INIT_WORK(&tmdev->sensor[i].notify_work, |
||
400 | + notify_uspace_tsens_fn); |
||
401 | + } |
||
402 | + |
||
403 | + reg_cntl = SW_RST; |
||
404 | + ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); |
||
405 | + if (ret) |
||
406 | + return ret; |
||
407 | + |
||
408 | + reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); |
||
409 | + reg_cntl &= ~SW_RST; |
||
410 | + ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, |
||
411 | + CONFIG_MASK, CONFIG); |
||
412 | + |
||
413 | + reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; |
||
414 | + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); |
||
415 | + if (ret) |
||
416 | + return ret; |
||
417 | + |
||
418 | + reg_cntl |= EN; |
||
419 | + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); |
||
420 | + if (ret) |
||
421 | + return ret; |
||
422 | + |
||
423 | + return 0; |
||
424 | +} |
||
425 | + |
||
426 | +static int calibrate_ipq8064(struct tsens_device *tmdev) |
||
427 | +{ |
||
428 | + int i; |
||
429 | + char *data, *data_backup; |
||
430 | + |
||
431 | + ssize_t num_read = tmdev->num_sensors; |
||
432 | + struct tsens_sensor *s = tmdev->sensor; |
||
433 | + |
||
434 | + data = qfprom_read(tmdev->dev, "calib"); |
||
435 | + if (IS_ERR(data)) { |
||
436 | + pr_err("Calibration not found.\n"); |
||
437 | + return PTR_ERR(data); |
||
438 | + } |
||
439 | + |
||
440 | + data_backup = qfprom_read(tmdev->dev, "calib_backup"); |
||
441 | + if (IS_ERR(data_backup)) { |
||
442 | + pr_err("Backup calibration not found.\n"); |
||
443 | + return PTR_ERR(data_backup); |
||
444 | + } |
||
445 | + |
||
446 | + for (i = 0; i < num_read; i++) { |
||
447 | + s[i].calib_data = readb_relaxed(data + i); |
||
448 | + s[i].calib_data_backup = readb_relaxed(data_backup + i); |
||
449 | + |
||
450 | + if (s[i].calib_data_backup) |
||
451 | + s[i].calib_data = s[i].calib_data_backup; |
||
452 | + if (!s[i].calib_data) { |
||
453 | + pr_err("QFPROM TSENS calibration data not present\n"); |
||
454 | + return -ENODEV; |
||
455 | + } |
||
456 | + s[i].slope = tsens_8064_slope[i]; |
||
457 | + s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope); |
||
458 | + } |
||
459 | + |
||
460 | + hw_init(tmdev); |
||
461 | + |
||
462 | + return 0; |
||
463 | +} |
||
464 | + |
||
465 | +static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp) |
||
466 | +{ |
||
467 | + int ret; |
||
468 | + u32 code, trdy; |
||
469 | + const struct tsens_sensor *s = &tmdev->sensor[id]; |
||
470 | + unsigned long timeout; |
||
471 | + |
||
472 | + timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); |
||
473 | + do { |
||
474 | + ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); |
||
475 | + if (ret) |
||
476 | + return ret; |
||
477 | + if (!(trdy & TRDY_MASK)) |
||
478 | + continue; |
||
479 | + ret = regmap_read(tmdev->map, s->status, &code); |
||
480 | + if (ret) |
||
481 | + return ret; |
||
482 | + *temp = code_to_degC(code, s); |
||
483 | + return 0; |
||
484 | + } while (time_before(jiffies, timeout)); |
||
485 | + |
||
486 | + return -ETIMEDOUT; |
||
487 | +} |
||
488 | + |
||
489 | +static int set_trip_temp_ipq8064(void *data, int trip, int temp) |
||
490 | +{ |
||
491 | + unsigned int reg_th, reg_cntl; |
||
492 | + int ret, code, code_chk, hi_code, lo_code; |
||
493 | + const struct tsens_sensor *s = data; |
||
494 | + struct tsens_device *tmdev = s->tmdev; |
||
495 | + |
||
496 | + code_chk = code = degC_to_code(temp, s); |
||
497 | + |
||
498 | + if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE) |
||
499 | + return -EINVAL; |
||
500 | + |
||
501 | + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); |
||
502 | + if (ret) |
||
503 | + return ret; |
||
504 | + |
||
505 | + ret = regmap_read(tmdev->map, THRESHOLD_ADDR, ®_th); |
||
506 | + if (ret) |
||
507 | + return ret; |
||
508 | + |
||
509 | + hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK) |
||
510 | + >> THRESHOLD_UPPER_LIMIT_SHIFT; |
||
511 | + lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK) |
||
512 | + >> THRESHOLD_LOWER_LIMIT_SHIFT; |
||
513 | + |
||
514 | + switch (trip) { |
||
515 | + case TSENS_TRIP_STAGE3: |
||
516 | + code <<= THRESHOLD_MAX_LIMIT_SHIFT; |
||
517 | + reg_th &= ~THRESHOLD_MAX_LIMIT_MASK; |
||
518 | + break; |
||
519 | + case TSENS_TRIP_STAGE2: |
||
520 | + if (code_chk <= lo_code) |
||
521 | + return -EINVAL; |
||
522 | + code <<= THRESHOLD_UPPER_LIMIT_SHIFT; |
||
523 | + reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK; |
||
524 | + break; |
||
525 | + case TSENS_TRIP_STAGE1: |
||
526 | + if (code_chk >= hi_code) |
||
527 | + return -EINVAL; |
||
528 | + code <<= THRESHOLD_LOWER_LIMIT_SHIFT; |
||
529 | + reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK; |
||
530 | + break; |
||
531 | + case TSENS_TRIP_STAGE0: |
||
532 | + code <<= THRESHOLD_MIN_LIMIT_SHIFT; |
||
533 | + reg_th &= ~THRESHOLD_MIN_LIMIT_MASK; |
||
534 | + break; |
||
535 | + default: |
||
536 | + return -EINVAL; |
||
537 | + } |
||
538 | + |
||
539 | + ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code); |
||
540 | + if (ret) |
||
541 | + return ret; |
||
542 | + |
||
543 | + return 0; |
||
544 | +} |
||
545 | + |
||
546 | +static int set_trip_activate_ipq8064(void *data, int trip, |
||
547 | + enum thermal_trip_activation_mode mode) |
||
548 | +{ |
||
549 | + unsigned int reg_cntl, mask, val; |
||
550 | + const struct tsens_sensor *s = data; |
||
551 | + struct tsens_device *tmdev = s->tmdev; |
||
552 | + int ret; |
||
553 | + |
||
554 | + if (!tmdev || trip < 0) |
||
555 | + return -EINVAL; |
||
556 | + |
||
557 | + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); |
||
558 | + if (ret) |
||
559 | + return ret; |
||
560 | + |
||
561 | + switch (trip) { |
||
562 | + case TSENS_TRIP_STAGE3: |
||
563 | + mask = MAX_STATUS_MASK; |
||
564 | + break; |
||
565 | + case TSENS_TRIP_STAGE2: |
||
566 | + mask = UPPER_STATUS_CLR; |
||
567 | + break; |
||
568 | + case TSENS_TRIP_STAGE1: |
||
569 | + mask = LOWER_STATUS_CLR; |
||
570 | + break; |
||
571 | + case TSENS_TRIP_STAGE0: |
||
572 | + mask = MIN_STATUS_MASK; |
||
573 | + break; |
||
574 | + default: |
||
575 | + return -EINVAL; |
||
576 | + } |
||
577 | + |
||
578 | + if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) |
||
579 | + val = reg_cntl | mask; |
||
580 | + else |
||
581 | + val = reg_cntl & ~mask; |
||
582 | + |
||
583 | + ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val); |
||
584 | + if (ret) |
||
585 | + return ret; |
||
586 | + |
||
587 | + /* force memory to sync */ |
||
588 | + mb(); |
||
589 | + return 0; |
||
590 | +} |
||
591 | + |
||
592 | +const struct tsens_ops ops_ipq8064 = { |
||
593 | + .init = init_ipq8064, |
||
594 | + .calibrate = calibrate_ipq8064, |
||
595 | + .get_temp = get_temp_ipq8064, |
||
596 | + .suspend = suspend_ipq8064, |
||
597 | + .resume = resume_ipq8064, |
||
598 | + .set_trip_temp = set_trip_temp_ipq8064, |
||
599 | + .set_trip_activate = set_trip_activate_ipq8064, |
||
600 | +}; |
||
601 | + |
||
602 | +const struct tsens_data data_ipq8064 = { |
||
603 | + .num_sensors = 11, |
||
604 | + .ops = &ops_ipq8064, |
||
605 | +}; |
||
606 | --- a/drivers/thermal/qcom/tsens.c |
||
607 | +++ b/drivers/thermal/qcom/tsens.c |
||
608 | @@ -72,6 +72,9 @@ static const struct of_device_id tsens_t |
||
609 | }, { |
||
610 | .compatible = "qcom,msm8996-tsens", |
||
611 | .data = &data_8996, |
||
612 | + }, { |
||
613 | + .compatible = "qcom,ipq8064-tsens", |
||
614 | + .data = &data_ipq8064, |
||
615 | }, |
||
616 | {} |
||
617 | }; |
||
618 | --- a/drivers/thermal/qcom/tsens.h |
||
619 | +++ b/drivers/thermal/qcom/tsens.h |
||
620 | @@ -89,6 +89,6 @@ void compute_intercept_slope(struct tsen |
||
621 | int init_common(struct tsens_device *); |
||
622 | int get_temp_common(struct tsens_device *, int, int *); |
||
623 | |||
624 | -extern const struct tsens_data data_8916, data_8974, data_8960, data_8996; |
||
625 | +extern const struct tsens_data data_8916, data_8974, data_8960, data_8996, data_ipq8064; |
||
626 | |||
627 | #endif /* __QCOM_TSENS_H__ */ |