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