2 * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <linux/delay.h>
15 #include <linux/err.h>
16 #include <linux/iio/consumer.h>
17 #include <linux/interrupt.h>
18 #include <linux/module.h>
20 #include <linux/of_device.h>
21 #include <linux/platform_device.h>
22 #include <linux/regmap.h>
23 #include <linux/thermal.h>
25 #define QPNP_TM_REG_TYPE 0x04
26 #define QPNP_TM_REG_SUBTYPE 0x05
27 #define QPNP_TM_REG_STATUS 0x08
28 #define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
29 #define QPNP_TM_REG_ALARM_CTRL 0x46
31 #define QPNP_TM_TYPE 0x09
32 #define QPNP_TM_SUBTYPE 0x08
34 #define STATUS_STAGE_MASK 0x03
36 #define SHUTDOWN_CTRL1_THRESHOLD_MASK 0x03
38 #define ALARM_CTRL_FORCE_ENABLE 0x80
41 * Trip point values based on threshold control
42 * 0 = {105 C, 125 C, 145 C}
43 * 1 = {110 C, 130 C, 150 C}
44 * 2 = {115 C, 135 C, 155 C}
45 * 3 = {120 C, 140 C, 160 C}
47 #define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
48 #define TEMP_STAGE_HYSTERESIS 2000
50 #define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
51 #define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
55 /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
56 #define DEFAULT_TEMP 37000
60 struct thermal_zone_device
*tz_dev
;
64 unsigned int prev_stage
;
66 struct iio_channel
*adc
;
69 static int qpnp_tm_read(struct qpnp_tm_chip
*chip
, u16 addr
, u8
*data
)
74 ret
= regmap_read(chip
->map
, chip
->base
+ addr
, &val
);
82 static int qpnp_tm_write(struct qpnp_tm_chip
*chip
, u16 addr
, u8 data
)
84 return regmap_write(chip
->map
, chip
->base
+ addr
, data
);
88 * This function updates the internal temp value based on the
89 * current thermal stage and threshold as well as the previous stage
91 static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip
*chip
)
97 ret
= qpnp_tm_read(chip
, QPNP_TM_REG_STATUS
, ®
);
101 stage
= reg
& STATUS_STAGE_MASK
;
103 if (stage
> chip
->stage
) {
104 /* increasing stage, use lower bound */
105 chip
->temp
= (stage
- 1) * TEMP_STAGE_STEP
+
106 chip
->thresh
* TEMP_THRESH_STEP
+
107 TEMP_STAGE_HYSTERESIS
+ TEMP_THRESH_MIN
;
108 } else if (stage
< chip
->stage
) {
109 /* decreasing stage, use upper bound */
110 chip
->temp
= stage
* TEMP_STAGE_STEP
+
111 chip
->thresh
* TEMP_THRESH_STEP
-
112 TEMP_STAGE_HYSTERESIS
+ TEMP_THRESH_MIN
;
120 static int qpnp_tm_get_temp(void *data
, int *temp
)
122 struct qpnp_tm_chip
*chip
= data
;
123 int ret
, mili_celsius
;
129 ret
= qpnp_tm_update_temp_no_adc(chip
);
133 ret
= iio_read_channel_processed(chip
->adc
, &mili_celsius
);
137 chip
->temp
= mili_celsius
;
140 *temp
= chip
->temp
< 0 ? 0 : chip
->temp
;
145 static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops
= {
146 .get_temp
= qpnp_tm_get_temp
,
149 static irqreturn_t
qpnp_tm_isr(int irq
, void *data
)
151 struct qpnp_tm_chip
*chip
= data
;
153 thermal_zone_device_update(chip
->tz_dev
, THERMAL_EVENT_UNSPECIFIED
);
159 * This function initializes the internal temp value based on only the
160 * current thermal stage and threshold. Setup threshold control and
161 * disable shutdown override.
163 static int qpnp_tm_init(struct qpnp_tm_chip
*chip
)
168 chip
->thresh
= THRESH_MIN
;
169 chip
->temp
= DEFAULT_TEMP
;
171 ret
= qpnp_tm_read(chip
, QPNP_TM_REG_STATUS
, ®
);
175 chip
->stage
= reg
& STATUS_STAGE_MASK
;
178 chip
->temp
= chip
->thresh
* TEMP_THRESH_STEP
+
179 (chip
->stage
- 1) * TEMP_STAGE_STEP
+
183 * Set threshold and disable software override of stage 2 and 3
186 reg
= chip
->thresh
& SHUTDOWN_CTRL1_THRESHOLD_MASK
;
187 ret
= qpnp_tm_write(chip
, QPNP_TM_REG_SHUTDOWN_CTRL1
, reg
);
191 /* Enable the thermal alarm PMIC module in always-on mode. */
192 reg
= ALARM_CTRL_FORCE_ENABLE
;
193 ret
= qpnp_tm_write(chip
, QPNP_TM_REG_ALARM_CTRL
, reg
);
198 static int qpnp_tm_probe(struct platform_device
*pdev
)
200 struct qpnp_tm_chip
*chip
;
201 struct device_node
*node
;
206 node
= pdev
->dev
.of_node
;
208 chip
= devm_kzalloc(&pdev
->dev
, sizeof(*chip
), GFP_KERNEL
);
212 dev_set_drvdata(&pdev
->dev
, chip
);
214 chip
->map
= dev_get_regmap(pdev
->dev
.parent
, NULL
);
218 ret
= of_property_read_u32(node
, "reg", &res
);
222 irq
= platform_get_irq(pdev
, 0);
226 /* ADC based measurements are optional */
227 chip
->adc
= devm_iio_channel_get(&pdev
->dev
, "thermal");
228 if (IS_ERR(chip
->adc
)) {
229 ret
= PTR_ERR(chip
->adc
);
231 if (ret
== -EPROBE_DEFER
)
237 ret
= qpnp_tm_read(chip
, QPNP_TM_REG_TYPE
, &type
);
239 dev_err(&pdev
->dev
, "could not read type\n");
243 ret
= qpnp_tm_read(chip
, QPNP_TM_REG_SUBTYPE
, &subtype
);
245 dev_err(&pdev
->dev
, "could not read subtype\n");
249 if (type
!= QPNP_TM_TYPE
|| subtype
!= QPNP_TM_SUBTYPE
) {
250 dev_err(&pdev
->dev
, "invalid type 0x%02x or subtype 0x%02x\n",
255 ret
= qpnp_tm_init(chip
);
257 dev_err(&pdev
->dev
, "init failed\n");
261 ret
= devm_request_threaded_irq(&pdev
->dev
, irq
, NULL
, qpnp_tm_isr
,
262 IRQF_ONESHOT
, node
->name
, chip
);
266 chip
->tz_dev
= devm_thermal_zone_of_sensor_register(&pdev
->dev
, 0, chip
,
267 &qpnp_tm_sensor_ops
);
268 if (IS_ERR(chip
->tz_dev
)) {
269 dev_err(&pdev
->dev
, "failed to register sensor\n");
270 return PTR_ERR(chip
->tz_dev
);
276 static const struct of_device_id qpnp_tm_match_table
[] = {
277 { .compatible
= "qcom,spmi-temp-alarm" },
280 MODULE_DEVICE_TABLE(of
, qpnp_tm_match_table
);
282 static struct platform_driver qpnp_tm_driver
= {
284 .name
= "spmi-temp-alarm",
285 .of_match_table
= qpnp_tm_match_table
,
287 .probe
= qpnp_tm_probe
,
289 module_platform_driver(qpnp_tm_driver
);
291 MODULE_ALIAS("platform:spmi-temp-alarm");
292 MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
293 MODULE_LICENSE("GPL v2");