]>
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 | ||
7a4ca51b | 128 | if (!chip->adc) { |
c610afaa II |
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 | ||
0e70f466 | 153 | thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED); |
c610afaa II |
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; | |
cd323b2b | 203 | u32 res; |
c610afaa II |
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 | ||
cd323b2b | 218 | ret = of_property_read_u32(node, "reg", &res); |
c610afaa II |
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 */ | |
7a4ca51b DL |
227 | chip->adc = devm_iio_channel_get(&pdev->dev, "thermal"); |
228 | if (IS_ERR(chip->adc)) { | |
229 | ret = PTR_ERR(chip->adc); | |
230 | chip->adc = NULL; | |
231 | if (ret == -EPROBE_DEFER) | |
232 | return ret; | |
233 | } | |
c610afaa | 234 | |
cd323b2b | 235 | chip->base = res; |
c610afaa II |
236 | |
237 | ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type); | |
238 | if (ret < 0) { | |
239 | dev_err(&pdev->dev, "could not read type\n"); | |
7a4ca51b | 240 | return ret; |
c610afaa II |
241 | } |
242 | ||
243 | ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype); | |
244 | if (ret < 0) { | |
245 | dev_err(&pdev->dev, "could not read subtype\n"); | |
7a4ca51b | 246 | return ret; |
c610afaa II |
247 | } |
248 | ||
249 | if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) { | |
250 | dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", | |
251 | type, subtype); | |
7a4ca51b | 252 | return -ENODEV; |
c610afaa II |
253 | } |
254 | ||
255 | ret = qpnp_tm_init(chip); | |
256 | if (ret < 0) { | |
257 | dev_err(&pdev->dev, "init failed\n"); | |
7a4ca51b | 258 | return ret; |
c610afaa II |
259 | } |
260 | ||
261 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr, | |
262 | IRQF_ONESHOT, node->name, chip); | |
263 | if (ret < 0) | |
7a4ca51b | 264 | return ret; |
c610afaa | 265 | |
e936491e | 266 | chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip, |
c610afaa II |
267 | &qpnp_tm_sensor_ops); |
268 | if (IS_ERR(chip->tz_dev)) { | |
269 | dev_err(&pdev->dev, "failed to register sensor\n"); | |
7a4ca51b | 270 | return PTR_ERR(chip->tz_dev); |
c610afaa II |
271 | } |
272 | ||
273 | return 0; | |
c610afaa II |
274 | } |
275 | ||
276 | static const struct of_device_id qpnp_tm_match_table[] = { | |
277 | { .compatible = "qcom,spmi-temp-alarm" }, | |
278 | { } | |
279 | }; | |
280 | MODULE_DEVICE_TABLE(of, qpnp_tm_match_table); | |
281 | ||
282 | static struct platform_driver qpnp_tm_driver = { | |
283 | .driver = { | |
284 | .name = "spmi-temp-alarm", | |
285 | .of_match_table = qpnp_tm_match_table, | |
286 | }, | |
287 | .probe = qpnp_tm_probe, | |
c610afaa II |
288 | }; |
289 | module_platform_driver(qpnp_tm_driver); | |
290 | ||
291 | MODULE_ALIAS("platform:spmi-temp-alarm"); | |
292 | MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver"); | |
293 | MODULE_LICENSE("GPL v2"); |