Commit | Line | Data |
---|---|---|
16d60ba8 DM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * max31827.c - Support for Maxim Low-Power Switch | |
4 | * | |
5 | * Copyright (c) 2023 Daniel Matyas <daniel.matyas@analog.com> | |
6 | */ | |
7 | ||
8 | #include <linux/bitfield.h> | |
9 | #include <linux/bitops.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/hwmon.h> | |
12 | #include <linux/i2c.h> | |
13 | #include <linux/mutex.h> | |
14 | #include <linux/regmap.h> | |
15 | ||
16 | #define MAX31827_T_REG 0x0 | |
17 | #define MAX31827_CONFIGURATION_REG 0x2 | |
18 | #define MAX31827_TH_REG 0x4 | |
19 | #define MAX31827_TL_REG 0x6 | |
20 | #define MAX31827_TH_HYST_REG 0x8 | |
21 | #define MAX31827_TL_HYST_REG 0xA | |
22 | ||
23 | #define MAX31827_CONFIGURATION_1SHOT_MASK BIT(0) | |
24 | #define MAX31827_CONFIGURATION_CNV_RATE_MASK GENMASK(3, 1) | |
25 | #define MAX31827_CONFIGURATION_U_TEMP_STAT_MASK BIT(14) | |
26 | #define MAX31827_CONFIGURATION_O_TEMP_STAT_MASK BIT(15) | |
27 | ||
28 | #define MAX31827_12_BIT_CNV_TIME 141 | |
29 | ||
30 | #define MAX31827_CNV_1_DIV_64_HZ 0x1 | |
31 | #define MAX31827_CNV_1_DIV_32_HZ 0x2 | |
32 | #define MAX31827_CNV_1_DIV_16_HZ 0x3 | |
33 | #define MAX31827_CNV_1_DIV_4_HZ 0x4 | |
34 | #define MAX31827_CNV_1_HZ 0x5 | |
35 | #define MAX31827_CNV_4_HZ 0x6 | |
36 | #define MAX31827_CNV_8_HZ 0x7 | |
37 | ||
38 | #define MAX31827_16_BIT_TO_M_DGR(x) (sign_extend32(x, 15) * 1000 / 16) | |
39 | #define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000) | |
40 | #define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0) | |
41 | ||
42 | struct max31827_state { | |
43 | /* | |
44 | * Prevent simultaneous access to the i2c client. | |
45 | */ | |
46 | struct mutex lock; | |
47 | struct regmap *regmap; | |
48 | bool enable; | |
49 | }; | |
50 | ||
51 | static const struct regmap_config max31827_regmap = { | |
52 | .reg_bits = 8, | |
53 | .val_bits = 16, | |
54 | .max_register = 0xA, | |
55 | }; | |
56 | ||
57 | static int write_alarm_val(struct max31827_state *st, unsigned int reg, | |
58 | long val) | |
59 | { | |
60 | unsigned int cfg; | |
61 | unsigned int tmp; | |
62 | int ret; | |
63 | ||
64 | val = MAX31827_M_DGR_TO_16_BIT(val); | |
65 | ||
66 | /* | |
67 | * Before the Temperature Threshold Alarm and Alarm Hysteresis Threshold | |
68 | * register values are changed over I2C, the part must be in shutdown | |
69 | * mode. | |
70 | * | |
71 | * Mutex is used to ensure, that some other process doesn't change the | |
72 | * configuration register. | |
73 | */ | |
74 | mutex_lock(&st->lock); | |
75 | ||
76 | if (!st->enable) { | |
77 | ret = regmap_write(st->regmap, reg, val); | |
78 | goto unlock; | |
79 | } | |
80 | ||
81 | ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &cfg); | |
82 | if (ret) | |
83 | goto unlock; | |
84 | ||
85 | tmp = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK | | |
86 | MAX31827_CONFIGURATION_CNV_RATE_MASK); | |
87 | ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, tmp); | |
88 | if (ret) | |
89 | goto unlock; | |
90 | ||
91 | ret = regmap_write(st->regmap, reg, val); | |
92 | if (ret) | |
93 | goto unlock; | |
94 | ||
95 | ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg); | |
96 | ||
97 | unlock: | |
98 | mutex_unlock(&st->lock); | |
99 | return ret; | |
100 | } | |
101 | ||
102 | static umode_t max31827_is_visible(const void *state, | |
103 | enum hwmon_sensor_types type, u32 attr, | |
104 | int channel) | |
105 | { | |
106 | if (type == hwmon_temp) { | |
107 | switch (attr) { | |
108 | case hwmon_temp_enable: | |
109 | case hwmon_temp_max: | |
110 | case hwmon_temp_min: | |
111 | case hwmon_temp_max_hyst: | |
112 | case hwmon_temp_min_hyst: | |
113 | return 0644; | |
114 | case hwmon_temp_input: | |
115 | case hwmon_temp_min_alarm: | |
116 | case hwmon_temp_max_alarm: | |
117 | return 0444; | |
118 | default: | |
119 | return 0; | |
120 | } | |
121 | } else if (type == hwmon_chip) { | |
122 | if (attr == hwmon_chip_update_interval) | |
123 | return 0644; | |
124 | } | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | static int max31827_read(struct device *dev, enum hwmon_sensor_types type, | |
130 | u32 attr, int channel, long *val) | |
131 | { | |
132 | struct max31827_state *st = dev_get_drvdata(dev); | |
133 | unsigned int uval; | |
134 | int ret = 0; | |
135 | ||
136 | switch (type) { | |
137 | case hwmon_temp: | |
138 | switch (attr) { | |
139 | case hwmon_temp_enable: | |
140 | ret = regmap_read(st->regmap, | |
141 | MAX31827_CONFIGURATION_REG, &uval); | |
142 | if (ret) | |
143 | break; | |
144 | ||
145 | uval = FIELD_GET(MAX31827_CONFIGURATION_1SHOT_MASK | | |
146 | MAX31827_CONFIGURATION_CNV_RATE_MASK, | |
147 | uval); | |
148 | *val = !!uval; | |
149 | ||
150 | break; | |
151 | case hwmon_temp_input: | |
152 | mutex_lock(&st->lock); | |
153 | ||
154 | if (!st->enable) { | |
155 | /* | |
156 | * This operation requires mutex protection, | |
157 | * because the chip configuration should not | |
158 | * be changed during the conversion process. | |
159 | */ | |
160 | ||
161 | ret = regmap_update_bits(st->regmap, | |
162 | MAX31827_CONFIGURATION_REG, | |
163 | MAX31827_CONFIGURATION_1SHOT_MASK, | |
164 | 1); | |
165 | if (ret) { | |
166 | mutex_unlock(&st->lock); | |
167 | return ret; | |
168 | } | |
169 | ||
170 | msleep(MAX31827_12_BIT_CNV_TIME); | |
171 | } | |
172 | ret = regmap_read(st->regmap, MAX31827_T_REG, &uval); | |
173 | ||
174 | mutex_unlock(&st->lock); | |
175 | ||
176 | if (ret) | |
177 | break; | |
178 | ||
179 | *val = MAX31827_16_BIT_TO_M_DGR(uval); | |
180 | ||
181 | break; | |
182 | case hwmon_temp_max: | |
183 | ret = regmap_read(st->regmap, MAX31827_TH_REG, &uval); | |
184 | if (ret) | |
185 | break; | |
186 | ||
187 | *val = MAX31827_16_BIT_TO_M_DGR(uval); | |
188 | break; | |
189 | case hwmon_temp_max_hyst: | |
190 | ret = regmap_read(st->regmap, MAX31827_TH_HYST_REG, | |
191 | &uval); | |
192 | if (ret) | |
193 | break; | |
194 | ||
195 | *val = MAX31827_16_BIT_TO_M_DGR(uval); | |
196 | break; | |
197 | case hwmon_temp_max_alarm: | |
198 | ret = regmap_read(st->regmap, | |
199 | MAX31827_CONFIGURATION_REG, &uval); | |
200 | if (ret) | |
201 | break; | |
202 | ||
203 | *val = FIELD_GET(MAX31827_CONFIGURATION_O_TEMP_STAT_MASK, | |
204 | uval); | |
205 | break; | |
206 | case hwmon_temp_min: | |
207 | ret = regmap_read(st->regmap, MAX31827_TL_REG, &uval); | |
208 | if (ret) | |
209 | break; | |
210 | ||
211 | *val = MAX31827_16_BIT_TO_M_DGR(uval); | |
212 | break; | |
213 | case hwmon_temp_min_hyst: | |
214 | ret = regmap_read(st->regmap, MAX31827_TL_HYST_REG, | |
215 | &uval); | |
216 | if (ret) | |
217 | break; | |
218 | ||
219 | *val = MAX31827_16_BIT_TO_M_DGR(uval); | |
220 | break; | |
221 | case hwmon_temp_min_alarm: | |
222 | ret = regmap_read(st->regmap, | |
223 | MAX31827_CONFIGURATION_REG, &uval); | |
224 | if (ret) | |
225 | break; | |
226 | ||
227 | *val = FIELD_GET(MAX31827_CONFIGURATION_U_TEMP_STAT_MASK, | |
228 | uval); | |
229 | break; | |
230 | default: | |
231 | ret = -EOPNOTSUPP; | |
232 | break; | |
233 | } | |
234 | ||
235 | break; | |
236 | ||
237 | case hwmon_chip: | |
238 | if (attr == hwmon_chip_update_interval) { | |
239 | ret = regmap_read(st->regmap, | |
240 | MAX31827_CONFIGURATION_REG, &uval); | |
241 | if (ret) | |
242 | break; | |
243 | ||
244 | uval = FIELD_GET(MAX31827_CONFIGURATION_CNV_RATE_MASK, | |
245 | uval); | |
246 | switch (uval) { | |
247 | case MAX31827_CNV_1_DIV_64_HZ: | |
248 | *val = 64000; | |
249 | break; | |
250 | case MAX31827_CNV_1_DIV_32_HZ: | |
251 | *val = 32000; | |
252 | break; | |
253 | case MAX31827_CNV_1_DIV_16_HZ: | |
254 | *val = 16000; | |
255 | break; | |
256 | case MAX31827_CNV_1_DIV_4_HZ: | |
257 | *val = 4000; | |
258 | break; | |
259 | case MAX31827_CNV_1_HZ: | |
260 | *val = 1000; | |
261 | break; | |
262 | case MAX31827_CNV_4_HZ: | |
263 | *val = 250; | |
264 | break; | |
265 | case MAX31827_CNV_8_HZ: | |
266 | *val = 125; | |
267 | break; | |
268 | default: | |
269 | *val = 0; | |
270 | break; | |
271 | } | |
272 | } | |
273 | break; | |
274 | ||
275 | default: | |
276 | ret = -EOPNOTSUPP; | |
277 | break; | |
278 | } | |
279 | ||
280 | return ret; | |
281 | } | |
282 | ||
283 | static int max31827_write(struct device *dev, enum hwmon_sensor_types type, | |
284 | u32 attr, int channel, long val) | |
285 | { | |
286 | struct max31827_state *st = dev_get_drvdata(dev); | |
287 | int ret; | |
288 | ||
289 | switch (type) { | |
290 | case hwmon_temp: | |
291 | switch (attr) { | |
292 | case hwmon_temp_enable: | |
293 | if (val >> 1) | |
294 | return -EINVAL; | |
295 | ||
296 | mutex_lock(&st->lock); | |
297 | /** | |
298 | * The chip should not be enabled while a conversion is | |
299 | * performed. Neither should the chip be enabled when | |
300 | * the alarm values are changed. | |
301 | */ | |
302 | ||
303 | st->enable = val; | |
304 | ||
305 | ret = regmap_update_bits(st->regmap, | |
306 | MAX31827_CONFIGURATION_REG, | |
307 | MAX31827_CONFIGURATION_1SHOT_MASK | | |
308 | MAX31827_CONFIGURATION_CNV_RATE_MASK, | |
309 | MAX31827_DEVICE_ENABLE(val)); | |
310 | ||
311 | mutex_unlock(&st->lock); | |
312 | ||
313 | return ret; | |
314 | ||
315 | case hwmon_temp_max: | |
316 | return write_alarm_val(st, MAX31827_TH_REG, val); | |
317 | ||
318 | case hwmon_temp_max_hyst: | |
319 | return write_alarm_val(st, MAX31827_TH_HYST_REG, val); | |
320 | ||
321 | case hwmon_temp_min: | |
322 | return write_alarm_val(st, MAX31827_TL_REG, val); | |
323 | ||
324 | case hwmon_temp_min_hyst: | |
325 | return write_alarm_val(st, MAX31827_TL_HYST_REG, val); | |
326 | ||
327 | default: | |
328 | return -EOPNOTSUPP; | |
329 | } | |
330 | ||
331 | case hwmon_chip: | |
332 | if (attr == hwmon_chip_update_interval) { | |
333 | if (!st->enable) | |
334 | return -EINVAL; | |
335 | ||
336 | switch (val) { | |
337 | case 125: | |
338 | val = MAX31827_CNV_8_HZ; | |
339 | break; | |
340 | case 250: | |
341 | val = MAX31827_CNV_4_HZ; | |
342 | break; | |
343 | case 1000: | |
344 | val = MAX31827_CNV_1_HZ; | |
345 | break; | |
346 | case 4000: | |
347 | val = MAX31827_CNV_1_DIV_4_HZ; | |
348 | break; | |
349 | case 16000: | |
350 | val = MAX31827_CNV_1_DIV_16_HZ; | |
351 | break; | |
352 | case 32000: | |
353 | val = MAX31827_CNV_1_DIV_32_HZ; | |
354 | break; | |
355 | case 64000: | |
356 | val = MAX31827_CNV_1_DIV_64_HZ; | |
357 | break; | |
358 | default: | |
359 | return -EINVAL; | |
360 | } | |
361 | ||
362 | val = FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK, | |
363 | val); | |
364 | ||
365 | return regmap_update_bits(st->regmap, | |
366 | MAX31827_CONFIGURATION_REG, | |
367 | MAX31827_CONFIGURATION_CNV_RATE_MASK, | |
368 | val); | |
369 | } | |
370 | break; | |
371 | ||
372 | default: | |
373 | return -EOPNOTSUPP; | |
374 | } | |
375 | ||
376 | return -EOPNOTSUPP; | |
377 | } | |
378 | ||
379 | static int max31827_init_client(struct max31827_state *st) | |
380 | { | |
381 | st->enable = true; | |
382 | ||
383 | return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, | |
384 | MAX31827_CONFIGURATION_1SHOT_MASK | | |
385 | MAX31827_CONFIGURATION_CNV_RATE_MASK, | |
386 | MAX31827_DEVICE_ENABLE(1)); | |
387 | } | |
388 | ||
389 | static const struct hwmon_channel_info *max31827_info[] = { | |
390 | HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_MIN | | |
391 | HWMON_T_MIN_HYST | HWMON_T_MIN_ALARM | | |
392 | HWMON_T_MAX | HWMON_T_MAX_HYST | | |
393 | HWMON_T_MAX_ALARM), | |
394 | HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), | |
395 | NULL, | |
396 | }; | |
397 | ||
398 | static const struct hwmon_ops max31827_hwmon_ops = { | |
399 | .is_visible = max31827_is_visible, | |
400 | .read = max31827_read, | |
401 | .write = max31827_write, | |
402 | }; | |
403 | ||
404 | static const struct hwmon_chip_info max31827_chip_info = { | |
405 | .ops = &max31827_hwmon_ops, | |
406 | .info = max31827_info, | |
407 | }; | |
408 | ||
409 | static int max31827_probe(struct i2c_client *client) | |
410 | { | |
411 | struct device *dev = &client->dev; | |
412 | struct device *hwmon_dev; | |
413 | struct max31827_state *st; | |
414 | int err; | |
415 | ||
416 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) | |
417 | return -EOPNOTSUPP; | |
418 | ||
419 | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); | |
420 | if (!st) | |
421 | return -ENOMEM; | |
422 | ||
423 | mutex_init(&st->lock); | |
424 | ||
425 | st->regmap = devm_regmap_init_i2c(client, &max31827_regmap); | |
426 | if (IS_ERR(st->regmap)) | |
427 | return dev_err_probe(dev, PTR_ERR(st->regmap), | |
428 | "Failed to allocate regmap.\n"); | |
429 | ||
430 | err = max31827_init_client(st); | |
431 | if (err) | |
432 | return err; | |
433 | ||
434 | hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, st, | |
435 | &max31827_chip_info, | |
436 | NULL); | |
437 | ||
438 | return PTR_ERR_OR_ZERO(hwmon_dev); | |
439 | } | |
440 | ||
441 | static const struct i2c_device_id max31827_i2c_ids[] = { | |
442 | { "max31827", 0 }, | |
443 | { } | |
444 | }; | |
445 | MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids); | |
446 | ||
447 | static const struct of_device_id max31827_of_match[] = { | |
448 | { .compatible = "adi,max31827" }, | |
449 | { } | |
450 | }; | |
451 | MODULE_DEVICE_TABLE(of, max31827_of_match); | |
452 | ||
453 | static struct i2c_driver max31827_driver = { | |
454 | .class = I2C_CLASS_HWMON, | |
455 | .driver = { | |
456 | .name = "max31827", | |
457 | .of_match_table = max31827_of_match, | |
458 | }, | |
90fc660e | 459 | .probe = max31827_probe, |
16d60ba8 DM |
460 | .id_table = max31827_i2c_ids, |
461 | }; | |
462 | module_i2c_driver(max31827_driver); | |
463 | ||
464 | MODULE_AUTHOR("Daniel Matyas <daniel.matyas@analog.com>"); | |
465 | MODULE_DESCRIPTION("Maxim MAX31827 low-power temperature switch driver"); | |
466 | MODULE_LICENSE("GPL"); |