Commit | Line | Data |
---|---|---|
c610afaa II |
1 | /* |
2 | * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. | |
3 | * | |
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. | |
7 | * | |
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. | |
12 | */ | |
13 | ||
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> | |
19 | #include <linux/of.h> | |
20 | #include <linux/of_device.h> | |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/regmap.h> | |
23 | #include <linux/thermal.h> | |
24 | ||
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 | |
30 | ||
31 | #define QPNP_TM_TYPE 0x09 | |
32 | #define QPNP_TM_SUBTYPE 0x08 | |
33 | ||
34 | #define STATUS_STAGE_MASK 0x03 | |
35 | ||
36 | #define SHUTDOWN_CTRL1_THRESHOLD_MASK 0x03 | |
37 | ||
38 | #define ALARM_CTRL_FORCE_ENABLE 0x80 | |
39 | ||
40 | /* | |
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} | |
46 | */ | |
47 | #define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */ | |
48 | #define TEMP_STAGE_HYSTERESIS 2000 | |
49 | ||
50 | #define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */ | |
51 | #define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */ | |
52 | ||
53 | #define THRESH_MIN 0 | |
54 | ||
55 | /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ | |
56 | #define DEFAULT_TEMP 37000 | |
57 | ||
58 | struct qpnp_tm_chip { | |
59 | struct regmap *map; | |
60 | struct thermal_zone_device *tz_dev; | |
61 | long temp; | |
62 | unsigned int thresh; | |
63 | unsigned int stage; | |
64 | unsigned int prev_stage; | |
65 | unsigned int base; | |
66 | struct iio_channel *adc; | |
67 | }; | |
68 | ||
69 | static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data) | |
70 | { | |
71 | unsigned int val; | |
72 | int ret; | |
73 | ||
74 | ret = regmap_read(chip->map, chip->base + addr, &val); | |
75 | if (ret < 0) | |
76 | return ret; | |
77 | ||
78 | *data = val; | |
79 | return 0; | |
80 | } | |
81 | ||
82 | static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data) | |
83 | { | |
84 | return regmap_write(chip->map, chip->base + addr, data); | |
85 | } | |
86 | ||
87 | /* | |
88 | * This function updates the internal temp value based on the | |
89 | * current thermal stage and threshold as well as the previous stage | |
90 | */ | |
91 | static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) | |
92 | { | |
93 | unsigned int stage; | |
94 | int ret; | |
95 | u8 reg = 0; | |
96 | ||
97 | ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®); | |
98 | if (ret < 0) | |
99 | return ret; | |
100 | ||
101 | stage = reg & STATUS_STAGE_MASK; | |
102 | ||
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; | |
113 | } | |
114 | ||
115 | chip->stage = stage; | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
17e8351a | 120 | static int qpnp_tm_get_temp(void *data, int *temp) |
c610afaa II |
121 | { |
122 | struct qpnp_tm_chip *chip = data; | |
123 | int ret, mili_celsius; | |
124 | ||
125 | if (!temp) | |
126 | return -EINVAL; | |
127 | ||
128 | if (IS_ERR(chip->adc)) { | |
129 | ret = qpnp_tm_update_temp_no_adc(chip); | |
130 | if (ret < 0) | |
131 | return ret; | |
132 | } else { | |
133 | ret = iio_read_channel_processed(chip->adc, &mili_celsius); | |
134 | if (ret < 0) | |
135 | return ret; | |
136 | ||
137 | chip->temp = mili_celsius; | |
138 | } | |
139 | ||
140 | *temp = chip->temp < 0 ? 0 : chip->temp; | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = { | |
146 | .get_temp = qpnp_tm_get_temp, | |
147 | }; | |
148 | ||
149 | static irqreturn_t qpnp_tm_isr(int irq, void *data) | |
150 | { | |
151 | struct qpnp_tm_chip *chip = data; | |
152 | ||
153 | thermal_zone_device_update(chip->tz_dev); | |
154 | ||
155 | return IRQ_HANDLED; | |
156 | } | |
157 | ||
158 | /* | |
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. | |
162 | */ | |
163 | static int qpnp_tm_init(struct qpnp_tm_chip *chip) | |
164 | { | |
165 | int ret; | |
166 | u8 reg; | |
167 | ||
168 | chip->thresh = THRESH_MIN; | |
169 | chip->temp = DEFAULT_TEMP; | |
170 | ||
171 | ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®); | |
172 | if (ret < 0) | |
173 | return ret; | |
174 | ||
175 | chip->stage = reg & STATUS_STAGE_MASK; | |
176 | ||
177 | if (chip->stage) | |
178 | chip->temp = chip->thresh * TEMP_THRESH_STEP + | |
179 | (chip->stage - 1) * TEMP_STAGE_STEP + | |
180 | TEMP_THRESH_MIN; | |
181 | ||
182 | /* | |
183 | * Set threshold and disable software override of stage 2 and 3 | |
184 | * shutdowns. | |
185 | */ | |
186 | reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK; | |
187 | ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg); | |
188 | if (ret < 0) | |
189 | return ret; | |
190 | ||
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); | |
194 | ||
195 | return ret; | |
196 | } | |
197 | ||
198 | static int qpnp_tm_probe(struct platform_device *pdev) | |
199 | { | |
200 | struct qpnp_tm_chip *chip; | |
201 | struct device_node *node; | |
202 | u8 type, subtype; | |
203 | u32 res[2]; | |
204 | int ret, irq; | |
205 | ||
206 | node = pdev->dev.of_node; | |
207 | ||
208 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); | |
209 | if (!chip) | |
210 | return -ENOMEM; | |
211 | ||
212 | dev_set_drvdata(&pdev->dev, chip); | |
213 | ||
214 | chip->map = dev_get_regmap(pdev->dev.parent, NULL); | |
215 | if (!chip->map) | |
216 | return -ENXIO; | |
217 | ||
218 | ret = of_property_read_u32_array(node, "reg", res, 2); | |
219 | if (ret < 0) | |
220 | return ret; | |
221 | ||
222 | irq = platform_get_irq(pdev, 0); | |
223 | if (irq < 0) | |
224 | return irq; | |
225 | ||
226 | /* ADC based measurements are optional */ | |
227 | chip->adc = iio_channel_get(&pdev->dev, "thermal"); | |
228 | if (PTR_ERR(chip->adc) == -EPROBE_DEFER) | |
229 | return PTR_ERR(chip->adc); | |
230 | ||
231 | chip->base = res[0]; | |
232 | ||
233 | ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type); | |
234 | if (ret < 0) { | |
235 | dev_err(&pdev->dev, "could not read type\n"); | |
236 | goto fail; | |
237 | } | |
238 | ||
239 | ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype); | |
240 | if (ret < 0) { | |
241 | dev_err(&pdev->dev, "could not read subtype\n"); | |
242 | goto fail; | |
243 | } | |
244 | ||
245 | if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) { | |
246 | dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", | |
247 | type, subtype); | |
248 | ret = -ENODEV; | |
249 | goto fail; | |
250 | } | |
251 | ||
252 | ret = qpnp_tm_init(chip); | |
253 | if (ret < 0) { | |
254 | dev_err(&pdev->dev, "init failed\n"); | |
255 | goto fail; | |
256 | } | |
257 | ||
258 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr, | |
259 | IRQF_ONESHOT, node->name, chip); | |
260 | if (ret < 0) | |
261 | goto fail; | |
262 | ||
263 | chip->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, chip, | |
264 | &qpnp_tm_sensor_ops); | |
265 | if (IS_ERR(chip->tz_dev)) { | |
266 | dev_err(&pdev->dev, "failed to register sensor\n"); | |
267 | ret = PTR_ERR(chip->tz_dev); | |
268 | goto fail; | |
269 | } | |
270 | ||
271 | return 0; | |
272 | ||
273 | fail: | |
274 | if (!IS_ERR(chip->adc)) | |
275 | iio_channel_release(chip->adc); | |
276 | ||
277 | return ret; | |
278 | } | |
279 | ||
280 | static int qpnp_tm_remove(struct platform_device *pdev) | |
281 | { | |
282 | struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev); | |
283 | ||
284 | thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev); | |
285 | if (!IS_ERR(chip->adc)) | |
286 | iio_channel_release(chip->adc); | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
291 | static const struct of_device_id qpnp_tm_match_table[] = { | |
292 | { .compatible = "qcom,spmi-temp-alarm" }, | |
293 | { } | |
294 | }; | |
295 | MODULE_DEVICE_TABLE(of, qpnp_tm_match_table); | |
296 | ||
297 | static struct platform_driver qpnp_tm_driver = { | |
298 | .driver = { | |
299 | .name = "spmi-temp-alarm", | |
300 | .of_match_table = qpnp_tm_match_table, | |
301 | }, | |
302 | .probe = qpnp_tm_probe, | |
303 | .remove = qpnp_tm_remove, | |
304 | }; | |
305 | module_platform_driver(qpnp_tm_driver); | |
306 | ||
307 | MODULE_ALIAS("platform:spmi-temp-alarm"); | |
308 | MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver"); | |
309 | MODULE_LICENSE("GPL v2"); |