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
6 Add TSENS driver template to support IPQ8064.
7 This is a base file copied from tsens-8960.c
9 Change-Id: I47c573fdfa2d898243c6a6ba952d1632f91391f7
10 Signed-off-by: Rajith Cherian <rajith@codeaurora.org>
12 ipq8064: tsens: TSENS driver support for IPQ8064
14 Support for IPQ8064 tsens driver. The driver works
15 with the thermal framework. The driver overrides the
16 following fucntionalities:
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.
24 Change-Id: I8bc7204fd627d10875ab13fc1de8cb6c2ed7a918
25 Signed-off-by: Rajith Cherian <rajith@codeaurora.org>
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
35 diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
36 index 292ed89..f4a76f6 100644
37 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
38 +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
39 @@ -5,6 +5,7 @@ Required properties:
40 - "qcom,msm8916-tsens" : For 8916 Family of SoCs
41 - "qcom,msm8974-tsens" : For 8974 Family of SoCs
42 - "qcom,msm8996-tsens" : For 8996 Family of SoCs
43 + - "qcom,ipq8064-tsens" : For IPQ8064
45 - reg: Address range of the thermal registers
46 - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
47 diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
48 index 2cc2193..cc07cf4 100644
49 --- a/drivers/thermal/qcom/Makefile
50 +++ b/drivers/thermal/qcom/Makefile
52 obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
53 -qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o
54 +qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o \
56 diff --git a/drivers/thermal/qcom/tsens-ipq8064.c b/drivers/thermal/qcom/tsens-ipq8064.c
58 index 0000000..c52888f
60 +++ b/drivers/thermal/qcom/tsens-ipq8064.c
63 + * Copyright (c) 2015, The Linux Foundation. All rights reserved.
65 + * This program is free software; you can redistribute it and/or modify
66 + * it under the terms of the GNU General Public License version 2 and
67 + * only version 2 as published by the Free Software Foundation.
69 + * This program is distributed in the hope that it will be useful,
70 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
71 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
72 + * GNU General Public License for more details.
76 +#include <linux/platform_device.h>
77 +#include <linux/delay.h>
78 +#include <linux/bitops.h>
79 +#include <linux/regmap.h>
80 +#include <linux/thermal.h>
81 +#include <linux/nvmem-consumer.h>
82 +#include <linux/io.h>
83 +#include <linux/interrupt.h>
86 +#define CAL_MDEGC 30000
88 +#define CONFIG_ADDR 0x3640
89 +/* CONFIG_ADDR bitmasks */
91 +#define CONFIG_MASK 0xf
92 +#define CONFIG_SHIFT 0
94 +#define STATUS_CNTL_8064 0x3660
95 +#define CNTL_ADDR 0x3620
96 +/* CNTL_ADDR bitmasks */
98 +#define SW_RST BIT(1)
99 +#define SENSOR0_EN BIT(3)
100 +#define SLP_CLK_ENA BIT(26)
101 +#define MEASURE_PERIOD 1
102 +#define SENSOR0_SHIFT 3
104 +/* INT_STATUS_ADDR bitmasks */
105 +#define MIN_STATUS_MASK BIT(0)
106 +#define LOWER_STATUS_CLR BIT(1)
107 +#define UPPER_STATUS_CLR BIT(2)
108 +#define MAX_STATUS_MASK BIT(3)
110 +#define THRESHOLD_ADDR 0x3624
111 +/* THRESHOLD_ADDR bitmasks */
112 +#define THRESHOLD_MAX_CODE 0xff
113 +#define THRESHOLD_MIN_CODE 0
114 +#define THRESHOLD_MAX_LIMIT_SHIFT 24
115 +#define THRESHOLD_MIN_LIMIT_SHIFT 16
116 +#define THRESHOLD_UPPER_LIMIT_SHIFT 8
117 +#define THRESHOLD_LOWER_LIMIT_SHIFT 0
118 +#define THRESHOLD_MAX_LIMIT_MASK (THRESHOLD_MAX_CODE << \
119 + THRESHOLD_MAX_LIMIT_SHIFT)
120 +#define THRESHOLD_MIN_LIMIT_MASK (THRESHOLD_MAX_CODE << \
121 + THRESHOLD_MIN_LIMIT_SHIFT)
122 +#define THRESHOLD_UPPER_LIMIT_MASK (THRESHOLD_MAX_CODE << \
123 + THRESHOLD_UPPER_LIMIT_SHIFT)
124 +#define THRESHOLD_LOWER_LIMIT_MASK (THRESHOLD_MAX_CODE << \
125 + THRESHOLD_LOWER_LIMIT_SHIFT)
127 +/* Initial temperature threshold values */
128 +#define LOWER_LIMIT_TH 0x9d /* 95C */
129 +#define UPPER_LIMIT_TH 0xa6 /* 105C */
130 +#define MIN_LIMIT_TH 0x0
131 +#define MAX_LIMIT_TH 0xff
133 +#define S0_STATUS_ADDR 0x3628
134 +#define STATUS_ADDR_OFFSET 2
135 +#define SENSOR_STATUS_SIZE 4
136 +#define INT_STATUS_ADDR 0x363c
137 +#define TRDY_MASK BIT(7)
138 +#define TIMEOUT_US 100
140 +#define TSENS_EN BIT(0)
141 +#define TSENS_SW_RST BIT(1)
142 +#define TSENS_ADC_CLK_SEL BIT(2)
143 +#define SENSOR0_EN BIT(3)
144 +#define SENSOR1_EN BIT(4)
145 +#define SENSOR2_EN BIT(5)
146 +#define SENSOR3_EN BIT(6)
147 +#define SENSOR4_EN BIT(7)
148 +#define SENSORS_EN (SENSOR0_EN | SENSOR1_EN | \
149 + SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
150 +#define TSENS_8064_SENSOR5_EN BIT(8)
151 +#define TSENS_8064_SENSOR6_EN BIT(9)
152 +#define TSENS_8064_SENSOR7_EN BIT(10)
153 +#define TSENS_8064_SENSOR8_EN BIT(11)
154 +#define TSENS_8064_SENSOR9_EN BIT(12)
155 +#define TSENS_8064_SENSOR10_EN BIT(13)
156 +#define TSENS_8064_SENSORS_EN (SENSORS_EN | \
157 + TSENS_8064_SENSOR5_EN | \
158 + TSENS_8064_SENSOR6_EN | \
159 + TSENS_8064_SENSOR7_EN | \
160 + TSENS_8064_SENSOR8_EN | \
161 + TSENS_8064_SENSOR9_EN | \
162 + TSENS_8064_SENSOR10_EN)
164 +#define TSENS_8064_SEQ_SENSORS 5
165 +#define TSENS_8064_S4_S5_OFFSET 40
166 +#define TSENS_FACTOR 1000
168 +/* Trips: from very hot to very cold */
169 +enum tsens_trip_type {
170 + TSENS_TRIP_STAGE3 = 0,
177 +u32 tsens_8064_slope[] = {
178 + 1176, 1176, 1154, 1176,
179 + 1111, 1132, 1132, 1199,
183 +/* Temperature on y axis and ADC-code on x-axis */
184 +static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s)
186 + int degcbeforefactor, degc;
188 + degcbeforefactor = (adc_code * s->slope) + s->offset;
190 + if (degcbeforefactor == 0)
191 + degc = degcbeforefactor;
192 + else if (degcbeforefactor > 0)
193 + degc = (degcbeforefactor + TSENS_FACTOR/2)
196 + degc = (degcbeforefactor - TSENS_FACTOR/2)
202 +static int degC_to_code(int degC, const struct tsens_sensor *s)
204 + int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2))
207 + if (code > THRESHOLD_MAX_CODE)
208 + code = THRESHOLD_MAX_CODE;
209 + else if (code < THRESHOLD_MIN_CODE)
210 + code = THRESHOLD_MIN_CODE;
214 +static int suspend_ipq8064(struct tsens_device *tmdev)
218 + struct regmap *map = tmdev->map;
220 + ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
224 + ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
228 + mask = SLP_CLK_ENA | EN;
230 + ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
237 +static int resume_ipq8064(struct tsens_device *tmdev)
240 + struct regmap *map = tmdev->map;
242 + ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
246 + ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
250 + ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
254 + ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
261 +static void notify_uspace_tsens_fn(struct work_struct *work)
263 + struct tsens_sensor *s = container_of(work, struct tsens_sensor,
266 + sysfs_notify(&s->tzd->device.kobj, NULL, "type");
269 +static void tsens_scheduler_fn(struct work_struct *work)
271 + struct tsens_device *tmdev = container_of(work, struct tsens_device,
273 + unsigned int threshold, threshold_low, code, reg, sensor, mask;
274 + unsigned int sensor_addr;
275 + bool upper_th_x, lower_th_x;
278 + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®);
281 + reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR;
282 + ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg);
286 + mask = ~(LOWER_STATUS_CLR | UPPER_STATUS_CLR);
287 + ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold);
290 + threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK)
291 + >> THRESHOLD_LOWER_LIMIT_SHIFT;
292 + threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK)
293 + >> THRESHOLD_UPPER_LIMIT_SHIFT;
295 + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®);
299 + ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor);
302 + sensor &= (uint32_t) TSENS_8064_SENSORS_EN;
303 + sensor >>= SENSOR0_SHIFT;
305 + /* Constraint: There is only 1 interrupt control register for all
306 + * 11 temperature sensor. So monitoring more than 1 sensor based
307 + * on interrupts will yield inconsistent result. To overcome this
308 + * issue we will monitor only sensor 0 which is the master sensor.
311 + /* Skip if the sensor is disabled */
313 + ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code);
316 + upper_th_x = code >= threshold;
317 + lower_th_x = code <= threshold_low;
319 + mask |= UPPER_STATUS_CLR;
321 + mask |= LOWER_STATUS_CLR;
322 + if (upper_th_x || lower_th_x) {
323 + /* Notify user space */
324 + schedule_work(&tmdev->sensor[0].notify_work);
325 + regmap_read(tmdev->map, sensor_addr, &adc_code);
326 + pr_debug("Trigger (%d degrees) for sensor %d\n",
327 + code_to_degC(adc_code, &tmdev->sensor[0]), 0);
330 + regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask);
332 + /* force memory to sync */
336 +static irqreturn_t tsens_isr(int irq, void *data)
338 + struct tsens_device *tmdev = data;
340 + schedule_work(&tmdev->tsens_work);
341 + return IRQ_HANDLED;
344 +static void hw_init(struct tsens_device *tmdev)
347 + unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
348 + unsigned int reg_status_cntl = 0;
350 + regmap_read(tmdev->map, CNTL_ADDR, ®_cntl);
351 + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST);
353 + reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18)
354 + | (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT);
355 + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
356 + regmap_read(tmdev->map, STATUS_CNTL_8064, ®_status_cntl);
357 + reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR
358 + | MIN_STATUS_MASK | MAX_STATUS_MASK;
359 + regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl);
360 + reg_cntl |= TSENS_EN;
361 + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
363 + regmap_read(tmdev->map, CONFIG_ADDR, ®_cfg);
364 + reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT);
365 + regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg);
367 + reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT)
368 + | (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT)
369 + | (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT)
370 + | (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT);
372 + regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr);
374 + ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr,
375 + IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev);
377 + pr_err("%s: request_irq FAIL: %d\n", __func__, ret);
381 + INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn);
384 +static int init_ipq8064(struct tsens_device *tmdev)
387 + u32 reg_cntl, offset = 0;
389 + init_common(tmdev);
394 + * The status registers for each sensor are discontiguous
395 + * because some SoCs have 5 sensors while others have more
396 + * but the control registers stay in the same place, i.e
397 + * directly after the first 5 status registers.
399 + for (i = 0; i < tmdev->num_sensors; i++) {
400 + if (i >= TSENS_8064_SEQ_SENSORS)
401 + offset = TSENS_8064_S4_S5_OFFSET;
403 + tmdev->sensor[i].status = S0_STATUS_ADDR + offset
404 + + (i << STATUS_ADDR_OFFSET);
405 + tmdev->sensor[i].slope = tsens_8064_slope[i];
406 + INIT_WORK(&tmdev->sensor[i].notify_work,
407 + notify_uspace_tsens_fn);
411 + ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
415 + reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
416 + reg_cntl &= ~SW_RST;
417 + ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
418 + CONFIG_MASK, CONFIG);
420 + reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
421 + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
426 + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
433 +static int calibrate_ipq8064(struct tsens_device *tmdev)
436 + char *data, *data_backup;
438 + ssize_t num_read = tmdev->num_sensors;
439 + struct tsens_sensor *s = tmdev->sensor;
441 + data = qfprom_read(tmdev->dev, "calib");
442 + if (IS_ERR(data)) {
443 + pr_err("Calibration not found.\n");
444 + return PTR_ERR(data);
447 + data_backup = qfprom_read(tmdev->dev, "calib_backup");
448 + if (IS_ERR(data_backup)) {
449 + pr_err("Backup calibration not found.\n");
450 + return PTR_ERR(data_backup);
453 + for (i = 0; i < num_read; i++) {
454 + s[i].calib_data = readb_relaxed(data + i);
455 + s[i].calib_data_backup = readb_relaxed(data_backup + i);
457 + if (s[i].calib_data_backup)
458 + s[i].calib_data = s[i].calib_data_backup;
459 + if (!s[i].calib_data) {
460 + pr_err("QFPROM TSENS calibration data not present\n");
463 + s[i].slope = tsens_8064_slope[i];
464 + s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope);
472 +static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp)
476 + const struct tsens_sensor *s = &tmdev->sensor[id];
477 + unsigned long timeout;
479 + timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
481 + ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
484 + if (!(trdy & TRDY_MASK))
486 + ret = regmap_read(tmdev->map, s->status, &code);
489 + *temp = code_to_degC(code, s);
491 + } while (time_before(jiffies, timeout));
496 +static int set_trip_temp_ipq8064(void *data, int trip, int temp)
498 + unsigned int reg_th, reg_cntl;
499 + int ret, code, code_chk, hi_code, lo_code;
500 + const struct tsens_sensor *s = data;
501 + struct tsens_device *tmdev = s->tmdev;
503 + code_chk = code = degC_to_code(temp, s);
505 + if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE)
508 + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl);
512 + ret = regmap_read(tmdev->map, THRESHOLD_ADDR, ®_th);
516 + hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK)
517 + >> THRESHOLD_UPPER_LIMIT_SHIFT;
518 + lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK)
519 + >> THRESHOLD_LOWER_LIMIT_SHIFT;
522 + case TSENS_TRIP_STAGE3:
523 + code <<= THRESHOLD_MAX_LIMIT_SHIFT;
524 + reg_th &= ~THRESHOLD_MAX_LIMIT_MASK;
526 + case TSENS_TRIP_STAGE2:
527 + if (code_chk <= lo_code)
529 + code <<= THRESHOLD_UPPER_LIMIT_SHIFT;
530 + reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK;
532 + case TSENS_TRIP_STAGE1:
533 + if (code_chk >= hi_code)
535 + code <<= THRESHOLD_LOWER_LIMIT_SHIFT;
536 + reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK;
538 + case TSENS_TRIP_STAGE0:
539 + code <<= THRESHOLD_MIN_LIMIT_SHIFT;
540 + reg_th &= ~THRESHOLD_MIN_LIMIT_MASK;
546 + ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code);
553 +static int set_trip_activate_ipq8064(void *data, int trip,
554 + enum thermal_trip_activation_mode mode)
556 + unsigned int reg_cntl, mask, val;
557 + const struct tsens_sensor *s = data;
558 + struct tsens_device *tmdev = s->tmdev;
561 + if (!tmdev || trip < 0)
564 + ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl);
569 + case TSENS_TRIP_STAGE3:
570 + mask = MAX_STATUS_MASK;
572 + case TSENS_TRIP_STAGE2:
573 + mask = UPPER_STATUS_CLR;
575 + case TSENS_TRIP_STAGE1:
576 + mask = LOWER_STATUS_CLR;
578 + case TSENS_TRIP_STAGE0:
579 + mask = MIN_STATUS_MASK;
585 + if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
586 + val = reg_cntl | mask;
588 + val = reg_cntl & ~mask;
590 + ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val);
594 + /* force memory to sync */
599 +const struct tsens_ops ops_ipq8064 = {
600 + .init = init_ipq8064,
601 + .calibrate = calibrate_ipq8064,
602 + .get_temp = get_temp_ipq8064,
603 + .suspend = suspend_ipq8064,
604 + .resume = resume_ipq8064,
605 + .set_trip_temp = set_trip_temp_ipq8064,
606 + .set_trip_activate = set_trip_activate_ipq8064,
609 +const struct tsens_data data_ipq8064 = {
611 + .ops = &ops_ipq8064,
613 diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
614 index 3f9fe6a..2d25593 100644
615 --- a/drivers/thermal/qcom/tsens.c
616 +++ b/drivers/thermal/qcom/tsens.c
617 @@ -72,6 +72,9 @@ static const struct of_device_id tsens_table[] = {
619 .compatible = "qcom,msm8996-tsens",
622 + .compatible = "qcom,ipq8064-tsens",
623 + .data = &data_ipq8064,
627 diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
628 index 911c197..31279a2 100644
629 --- a/drivers/thermal/qcom/tsens.h
630 +++ b/drivers/thermal/qcom/tsens.h
631 @@ -89,6 +89,6 @@ void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
632 int init_common(struct tsens_device *);
633 int get_temp_common(struct tsens_device *, int, int *);
635 -extern const struct tsens_data data_8916, data_8974, data_8960, data_8996;
636 +extern const struct tsens_data data_8916, data_8974, data_8960, data_8996, data_ipq8064;
638 #endif /* __QCOM_TSENS_H__ */