]>
Commit | Line | Data |
---|---|---|
20d4fd84 RN |
1 | /* |
2 | * Copyright (c) 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 | ||
15 | #include <linux/platform_device.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/bitops.h> | |
18 | #include <linux/regmap.h> | |
19 | #include <linux/thermal.h> | |
20 | #include "tsens.h" | |
21 | ||
22 | #define CAL_MDEGC 30000 | |
23 | ||
24 | #define CONFIG_ADDR 0x3640 | |
25 | #define CONFIG_ADDR_8660 0x3620 | |
26 | /* CONFIG_ADDR bitmasks */ | |
27 | #define CONFIG 0x9b | |
28 | #define CONFIG_MASK 0xf | |
29 | #define CONFIG_8660 1 | |
30 | #define CONFIG_SHIFT_8660 28 | |
31 | #define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) | |
32 | ||
33 | #define STATUS_CNTL_ADDR_8064 0x3660 | |
34 | #define CNTL_ADDR 0x3620 | |
35 | /* CNTL_ADDR bitmasks */ | |
36 | #define EN BIT(0) | |
37 | #define SW_RST BIT(1) | |
38 | #define SENSOR0_EN BIT(3) | |
39 | #define SLP_CLK_ENA BIT(26) | |
40 | #define SLP_CLK_ENA_8660 BIT(24) | |
41 | #define MEASURE_PERIOD 1 | |
42 | #define SENSOR0_SHIFT 3 | |
43 | ||
44 | /* INT_STATUS_ADDR bitmasks */ | |
45 | #define MIN_STATUS_MASK BIT(0) | |
46 | #define LOWER_STATUS_CLR BIT(1) | |
47 | #define UPPER_STATUS_CLR BIT(2) | |
48 | #define MAX_STATUS_MASK BIT(3) | |
49 | ||
50 | #define THRESHOLD_ADDR 0x3624 | |
51 | /* THRESHOLD_ADDR bitmasks */ | |
52 | #define THRESHOLD_MAX_LIMIT_SHIFT 24 | |
53 | #define THRESHOLD_MIN_LIMIT_SHIFT 16 | |
54 | #define THRESHOLD_UPPER_LIMIT_SHIFT 8 | |
55 | #define THRESHOLD_LOWER_LIMIT_SHIFT 0 | |
56 | ||
57 | /* Initial temperature threshold values */ | |
58 | #define LOWER_LIMIT_TH 0x50 | |
59 | #define UPPER_LIMIT_TH 0xdf | |
60 | #define MIN_LIMIT_TH 0x0 | |
61 | #define MAX_LIMIT_TH 0xff | |
62 | ||
63 | #define S0_STATUS_ADDR 0x3628 | |
64 | #define INT_STATUS_ADDR 0x363c | |
65 | #define TRDY_MASK BIT(7) | |
66 | #define TIMEOUT_US 100 | |
67 | ||
68 | static int suspend_8960(struct tsens_device *tmdev) | |
69 | { | |
70 | int ret; | |
71 | unsigned int mask; | |
72 | struct regmap *map = tmdev->map; | |
73 | ||
74 | ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); | |
75 | if (ret) | |
76 | return ret; | |
77 | ||
78 | ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); | |
79 | if (ret) | |
80 | return ret; | |
81 | ||
82 | if (tmdev->num_sensors > 1) | |
83 | mask = SLP_CLK_ENA | EN; | |
84 | else | |
85 | mask = SLP_CLK_ENA_8660 | EN; | |
86 | ||
87 | ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); | |
88 | if (ret) | |
89 | return ret; | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
94 | static int resume_8960(struct tsens_device *tmdev) | |
95 | { | |
96 | int ret; | |
97 | struct regmap *map = tmdev->map; | |
98 | ||
99 | ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); | |
100 | if (ret) | |
101 | return ret; | |
102 | ||
103 | /* | |
104 | * Separate CONFIG restore is not needed only for 8660 as | |
105 | * config is part of CTRL Addr and its restored as such | |
106 | */ | |
107 | if (tmdev->num_sensors > 1) { | |
108 | ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); | |
109 | if (ret) | |
110 | return ret; | |
111 | } | |
112 | ||
113 | ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); | |
114 | if (ret) | |
115 | return ret; | |
116 | ||
117 | ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); | |
118 | if (ret) | |
119 | return ret; | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static int enable_8960(struct tsens_device *tmdev, int id) | |
125 | { | |
126 | int ret; | |
127 | u32 reg, mask; | |
128 | ||
129 | ret = regmap_read(tmdev->map, CNTL_ADDR, ®); | |
130 | if (ret) | |
131 | return ret; | |
132 | ||
133 | mask = BIT(id + SENSOR0_SHIFT); | |
134 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST); | |
135 | if (ret) | |
136 | return ret; | |
137 | ||
138 | if (tmdev->num_sensors > 1) | |
139 | reg |= mask | SLP_CLK_ENA | EN; | |
140 | else | |
141 | reg |= mask | SLP_CLK_ENA_8660 | EN; | |
142 | ||
143 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg); | |
144 | if (ret) | |
145 | return ret; | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static void disable_8960(struct tsens_device *tmdev) | |
151 | { | |
152 | int ret; | |
153 | u32 reg_cntl; | |
154 | u32 mask; | |
155 | ||
156 | mask = GENMASK(tmdev->num_sensors - 1, 0); | |
157 | mask <<= SENSOR0_SHIFT; | |
158 | mask |= EN; | |
159 | ||
160 | ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); | |
161 | if (ret) | |
162 | return; | |
163 | ||
164 | reg_cntl &= ~mask; | |
165 | ||
166 | if (tmdev->num_sensors > 1) | |
167 | reg_cntl &= ~SLP_CLK_ENA; | |
168 | else | |
169 | reg_cntl &= ~SLP_CLK_ENA_8660; | |
170 | ||
171 | regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | |
172 | } | |
173 | ||
174 | static int init_8960(struct tsens_device *tmdev) | |
175 | { | |
176 | int ret, i; | |
177 | u32 reg_cntl; | |
178 | ||
179 | tmdev->map = dev_get_regmap(tmdev->dev, NULL); | |
180 | if (!tmdev->map) | |
181 | return -ENODEV; | |
182 | ||
183 | /* | |
184 | * The status registers for each sensor are discontiguous | |
185 | * because some SoCs have 5 sensors while others have more | |
186 | * but the control registers stay in the same place, i.e | |
187 | * directly after the first 5 status registers. | |
188 | */ | |
189 | for (i = 0; i < tmdev->num_sensors; i++) { | |
190 | if (i >= 5) | |
191 | tmdev->sensor[i].status = S0_STATUS_ADDR + 40; | |
192 | tmdev->sensor[i].status += i * 4; | |
193 | } | |
194 | ||
195 | reg_cntl = SW_RST; | |
196 | ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); | |
197 | if (ret) | |
198 | return ret; | |
199 | ||
200 | if (tmdev->num_sensors > 1) { | |
201 | reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); | |
202 | reg_cntl &= ~SW_RST; | |
203 | ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, | |
204 | CONFIG_MASK, CONFIG); | |
205 | } else { | |
206 | reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); | |
207 | reg_cntl &= ~CONFIG_MASK_8660; | |
208 | reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; | |
209 | } | |
210 | ||
211 | reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; | |
212 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | |
213 | if (ret) | |
214 | return ret; | |
215 | ||
216 | reg_cntl |= EN; | |
217 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | |
218 | if (ret) | |
219 | return ret; | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | static int calibrate_8960(struct tsens_device *tmdev) | |
225 | { | |
226 | int i; | |
227 | char *data; | |
228 | ||
229 | ssize_t num_read = tmdev->num_sensors; | |
230 | struct tsens_sensor *s = tmdev->sensor; | |
231 | ||
232 | data = qfprom_read(tmdev->dev, "calib"); | |
233 | if (IS_ERR(data)) | |
234 | data = qfprom_read(tmdev->dev, "calib_backup"); | |
235 | if (IS_ERR(data)) | |
236 | return PTR_ERR(data); | |
237 | ||
238 | for (i = 0; i < num_read; i++, s++) | |
239 | s->offset = data[i]; | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
244 | /* Temperature on y axis and ADC-code on x-axis */ | |
245 | static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) | |
246 | { | |
247 | int slope, offset; | |
248 | ||
249 | slope = thermal_zone_get_slope(s->tzd); | |
250 | offset = CAL_MDEGC - slope * s->offset; | |
251 | ||
252 | return adc_code * slope + offset; | |
253 | } | |
254 | ||
255 | static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) | |
256 | { | |
257 | int ret; | |
258 | u32 code, trdy; | |
259 | const struct tsens_sensor *s = &tmdev->sensor[id]; | |
260 | unsigned long timeout; | |
261 | ||
262 | timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); | |
263 | do { | |
264 | ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); | |
265 | if (ret) | |
266 | return ret; | |
267 | if (!(trdy & TRDY_MASK)) | |
268 | continue; | |
269 | ret = regmap_read(tmdev->map, s->status, &code); | |
270 | if (ret) | |
271 | return ret; | |
272 | *temp = code_to_mdegC(code, s); | |
273 | return 0; | |
274 | } while (time_before(jiffies, timeout)); | |
275 | ||
276 | return -ETIMEDOUT; | |
277 | } | |
278 | ||
032d4057 | 279 | static const struct tsens_ops ops_8960 = { |
20d4fd84 RN |
280 | .init = init_8960, |
281 | .calibrate = calibrate_8960, | |
282 | .get_temp = get_temp_8960, | |
283 | .enable = enable_8960, | |
284 | .disable = disable_8960, | |
285 | .suspend = suspend_8960, | |
286 | .resume = resume_8960, | |
287 | }; | |
288 | ||
289 | const struct tsens_data data_8960 = { | |
290 | .num_sensors = 11, | |
291 | .ops = &ops_8960, | |
292 | }; |