Commit | Line | Data |
---|---|---|
fb55a513 PC |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* max31856.c | |
3 | * | |
4 | * Maxim MAX31856 thermocouple sensor driver | |
5 | * | |
6 | * Copyright (C) 2018-2019 Rockwell Collins | |
7 | */ | |
8 | ||
ea410307 | 9 | #include <linux/ctype.h> |
60a0548f | 10 | #include <linux/mod_devicetable.h> |
fb55a513 PC |
11 | #include <linux/module.h> |
12 | #include <linux/init.h> | |
13 | #include <linux/err.h> | |
60a0548f | 14 | #include <linux/property.h> |
fb55a513 PC |
15 | #include <linux/spi/spi.h> |
16 | #include <linux/iio/iio.h> | |
17 | #include <linux/iio/sysfs.h> | |
57a4274c | 18 | #include <linux/util_macros.h> |
92b7d5b7 | 19 | #include <asm/unaligned.h> |
fb55a513 PC |
20 | #include <dt-bindings/iio/temperature/thermocouple.h> |
21 | /* | |
22 | * The MSB of the register value determines whether the following byte will | |
23 | * be written or read. If it is 0, one or more byte reads will follow. | |
24 | */ | |
25 | #define MAX31856_RD_WR_BIT BIT(7) | |
26 | ||
27 | #define MAX31856_CR0_AUTOCONVERT BIT(7) | |
28 | #define MAX31856_CR0_1SHOT BIT(6) | |
29 | #define MAX31856_CR0_OCFAULT BIT(4) | |
30 | #define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4) | |
76aa41c1 | 31 | #define MAX31856_CR0_FILTER_50HZ BIT(0) |
57a4274c AM |
32 | #define MAX31856_AVERAGING_MASK GENMASK(6, 4) |
33 | #define MAX31856_AVERAGING_SHIFT 4 | |
fb55a513 PC |
34 | #define MAX31856_TC_TYPE_MASK GENMASK(3, 0) |
35 | #define MAX31856_FAULT_OVUV BIT(1) | |
36 | #define MAX31856_FAULT_OPEN BIT(0) | |
37 | ||
38 | /* The MAX31856 registers */ | |
39 | #define MAX31856_CR0_REG 0x00 | |
40 | #define MAX31856_CR1_REG 0x01 | |
41 | #define MAX31856_MASK_REG 0x02 | |
42 | #define MAX31856_CJHF_REG 0x03 | |
43 | #define MAX31856_CJLF_REG 0x04 | |
44 | #define MAX31856_LTHFTH_REG 0x05 | |
45 | #define MAX31856_LTHFTL_REG 0x06 | |
46 | #define MAX31856_LTLFTH_REG 0x07 | |
47 | #define MAX31856_LTLFTL_REG 0x08 | |
48 | #define MAX31856_CJTO_REG 0x09 | |
49 | #define MAX31856_CJTH_REG 0x0A | |
50 | #define MAX31856_CJTL_REG 0x0B | |
51 | #define MAX31856_LTCBH_REG 0x0C | |
52 | #define MAX31856_LTCBM_REG 0x0D | |
53 | #define MAX31856_LTCBL_REG 0x0E | |
54 | #define MAX31856_SR_REG 0x0F | |
55 | ||
56 | static const struct iio_chan_spec max31856_channels[] = { | |
57 | { /* Thermocouple Temperature */ | |
58 | .type = IIO_TEMP, | |
59 | .info_mask_separate = | |
ea410307 AM |
60 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | |
61 | BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), | |
57a4274c AM |
62 | .info_mask_shared_by_type = |
63 | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | |
fb55a513 PC |
64 | }, |
65 | { /* Cold Junction Temperature */ | |
66 | .type = IIO_TEMP, | |
67 | .channel2 = IIO_MOD_TEMP_AMBIENT, | |
68 | .modified = 1, | |
69 | .info_mask_separate = | |
70 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), | |
57a4274c AM |
71 | .info_mask_shared_by_type = |
72 | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | |
fb55a513 PC |
73 | }, |
74 | }; | |
75 | ||
76 | struct max31856_data { | |
77 | struct spi_device *spi; | |
78 | u32 thermocouple_type; | |
76aa41c1 | 79 | bool filter_50hz; |
57a4274c | 80 | int averaging; |
fb55a513 PC |
81 | }; |
82 | ||
ea410307 AM |
83 | static const char max31856_tc_types[] = { |
84 | 'B', 'E', 'J', 'K', 'N', 'R', 'S', 'T' | |
85 | }; | |
86 | ||
fb55a513 PC |
87 | static int max31856_read(struct max31856_data *data, u8 reg, |
88 | u8 val[], unsigned int read_size) | |
89 | { | |
90 | return spi_write_then_read(data->spi, ®, 1, val, read_size); | |
91 | } | |
92 | ||
93 | static int max31856_write(struct max31856_data *data, u8 reg, | |
94 | unsigned int val) | |
95 | { | |
96 | u8 buf[2]; | |
97 | ||
98 | buf[0] = reg | (MAX31856_RD_WR_BIT); | |
99 | buf[1] = val; | |
100 | ||
101 | return spi_write(data->spi, buf, 2); | |
102 | } | |
103 | ||
104 | static int max31856_init(struct max31856_data *data) | |
105 | { | |
106 | int ret; | |
107 | u8 reg_cr0_val, reg_cr1_val; | |
108 | ||
109 | /* Start by changing to Off mode before making changes as | |
110 | * some settings are recommended to be set only when the device | |
111 | * is off | |
112 | */ | |
113 | ret = max31856_read(data, MAX31856_CR0_REG, ®_cr0_val, 1); | |
114 | if (ret) | |
115 | return ret; | |
116 | ||
117 | reg_cr0_val &= ~MAX31856_CR0_AUTOCONVERT; | |
118 | ret = max31856_write(data, MAX31856_CR0_REG, reg_cr0_val); | |
119 | if (ret) | |
120 | return ret; | |
121 | ||
122 | /* Set thermocouple type based on dts property */ | |
123 | ret = max31856_read(data, MAX31856_CR1_REG, ®_cr1_val, 1); | |
124 | if (ret) | |
125 | return ret; | |
126 | ||
127 | reg_cr1_val &= ~MAX31856_TC_TYPE_MASK; | |
128 | reg_cr1_val |= data->thermocouple_type; | |
57a4274c AM |
129 | |
130 | reg_cr1_val &= ~MAX31856_AVERAGING_MASK; | |
131 | reg_cr1_val |= data->averaging << MAX31856_AVERAGING_SHIFT; | |
132 | ||
fb55a513 PC |
133 | ret = max31856_write(data, MAX31856_CR1_REG, reg_cr1_val); |
134 | if (ret) | |
135 | return ret; | |
136 | ||
137 | /* | |
138 | * Enable Open circuit fault detection | |
139 | * Read datasheet for more information: Table 4. | |
140 | * Value 01 means : Enabled (Once every 16 conversions) | |
141 | */ | |
142 | reg_cr0_val &= ~MAX31856_CR0_OCFAULT_MASK; | |
143 | reg_cr0_val |= MAX31856_CR0_OCFAULT; | |
144 | ||
145 | /* Set Auto Conversion Mode */ | |
146 | reg_cr0_val &= ~MAX31856_CR0_1SHOT; | |
147 | reg_cr0_val |= MAX31856_CR0_AUTOCONVERT; | |
148 | ||
76aa41c1 AM |
149 | if (data->filter_50hz) |
150 | reg_cr0_val |= MAX31856_CR0_FILTER_50HZ; | |
151 | else | |
152 | reg_cr0_val &= ~MAX31856_CR0_FILTER_50HZ; | |
153 | ||
fb55a513 PC |
154 | return max31856_write(data, MAX31856_CR0_REG, reg_cr0_val); |
155 | } | |
156 | ||
157 | static int max31856_thermocouple_read(struct max31856_data *data, | |
158 | struct iio_chan_spec const *chan, | |
159 | int *val) | |
160 | { | |
161 | int ret, offset_cjto; | |
162 | u8 reg_val[3]; | |
163 | ||
164 | switch (chan->channel2) { | |
165 | case IIO_NO_MOD: | |
166 | /* | |
167 | * Multibyte Read | |
168 | * MAX31856_LTCBH_REG, MAX31856_LTCBM_REG, MAX31856_LTCBL_REG | |
169 | */ | |
170 | ret = max31856_read(data, MAX31856_LTCBH_REG, reg_val, 3); | |
171 | if (ret) | |
172 | return ret; | |
173 | /* Skip last 5 dead bits of LTCBL */ | |
92b7d5b7 | 174 | *val = get_unaligned_be24(®_val[0]) >> 5; |
fb55a513 PC |
175 | /* Check 7th bit of LTCBH reg. value for sign*/ |
176 | if (reg_val[0] & 0x80) | |
177 | *val -= 0x80000; | |
178 | break; | |
179 | ||
180 | case IIO_MOD_TEMP_AMBIENT: | |
181 | /* | |
182 | * Multibyte Read | |
183 | * MAX31856_CJTO_REG, MAX31856_CJTH_REG, MAX31856_CJTL_REG | |
184 | */ | |
185 | ret = max31856_read(data, MAX31856_CJTO_REG, reg_val, 3); | |
186 | if (ret) | |
187 | return ret; | |
188 | /* Get Cold Junction Temp. offset register value */ | |
189 | offset_cjto = reg_val[0]; | |
190 | /* Get CJTH and CJTL value and skip last 2 dead bits of CJTL */ | |
92b7d5b7 | 191 | *val = get_unaligned_be16(®_val[1]) >> 2; |
fb55a513 PC |
192 | /* As per datasheet add offset into CJTH and CJTL */ |
193 | *val += offset_cjto; | |
194 | /* Check 7th bit of CJTH reg. value for sign */ | |
195 | if (reg_val[1] & 0x80) | |
196 | *val -= 0x4000; | |
197 | break; | |
198 | ||
199 | default: | |
200 | return -EINVAL; | |
201 | } | |
202 | ||
203 | ret = max31856_read(data, MAX31856_SR_REG, reg_val, 1); | |
204 | if (ret) | |
205 | return ret; | |
206 | /* Check for over/under voltage or open circuit fault */ | |
207 | if (reg_val[0] & (MAX31856_FAULT_OVUV | MAX31856_FAULT_OPEN)) | |
208 | return -EIO; | |
209 | ||
210 | return ret; | |
211 | } | |
212 | ||
213 | static int max31856_read_raw(struct iio_dev *indio_dev, | |
214 | struct iio_chan_spec const *chan, | |
215 | int *val, int *val2, long mask) | |
216 | { | |
217 | struct max31856_data *data = iio_priv(indio_dev); | |
218 | int ret; | |
219 | ||
220 | switch (mask) { | |
221 | case IIO_CHAN_INFO_RAW: | |
222 | ret = max31856_thermocouple_read(data, chan, val); | |
223 | if (ret) | |
224 | return ret; | |
225 | return IIO_VAL_INT; | |
226 | case IIO_CHAN_INFO_SCALE: | |
227 | switch (chan->channel2) { | |
228 | case IIO_MOD_TEMP_AMBIENT: | |
229 | /* Cold junction Temp. Data resolution is 0.015625 */ | |
230 | *val = 15; | |
231 | *val2 = 625000; /* 1000 * 0.015625 */ | |
232 | ret = IIO_VAL_INT_PLUS_MICRO; | |
233 | break; | |
234 | default: | |
235 | /* Thermocouple Temp. Data resolution is 0.0078125 */ | |
236 | *val = 7; | |
237 | *val2 = 812500; /* 1000 * 0.0078125) */ | |
238 | return IIO_VAL_INT_PLUS_MICRO; | |
239 | } | |
240 | break; | |
57a4274c AM |
241 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
242 | *val = 1 << data->averaging; | |
243 | return IIO_VAL_INT; | |
ea410307 AM |
244 | case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: |
245 | *val = max31856_tc_types[data->thermocouple_type]; | |
246 | return IIO_VAL_CHAR; | |
8e4fefec CIK |
247 | default: |
248 | ret = -EINVAL; | |
249 | break; | |
fb55a513 PC |
250 | } |
251 | ||
252 | return ret; | |
253 | } | |
254 | ||
ea410307 AM |
255 | static int max31856_write_raw_get_fmt(struct iio_dev *indio_dev, |
256 | struct iio_chan_spec const *chan, | |
257 | long mask) | |
258 | { | |
259 | switch (mask) { | |
260 | case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: | |
261 | return IIO_VAL_CHAR; | |
262 | default: | |
263 | return IIO_VAL_INT; | |
264 | } | |
265 | } | |
266 | ||
57a4274c AM |
267 | static int max31856_write_raw(struct iio_dev *indio_dev, |
268 | struct iio_chan_spec const *chan, | |
269 | int val, int val2, long mask) | |
270 | { | |
271 | struct max31856_data *data = iio_priv(indio_dev); | |
272 | int msb; | |
273 | ||
274 | switch (mask) { | |
275 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: | |
276 | if (val > 16 || val < 1) | |
277 | return -EINVAL; | |
278 | msb = fls(val) - 1; | |
279 | /* Round up to next 2pow if needed */ | |
280 | if (BIT(msb) < val) | |
281 | msb++; | |
282 | ||
283 | data->averaging = msb; | |
284 | max31856_init(data); | |
285 | break; | |
ea410307 AM |
286 | case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: |
287 | { | |
288 | int tc_type = -1; | |
289 | int i; | |
290 | ||
291 | for (i = 0; i < ARRAY_SIZE(max31856_tc_types); i++) { | |
292 | if (max31856_tc_types[i] == toupper(val)) { | |
293 | tc_type = i; | |
294 | break; | |
295 | } | |
296 | } | |
297 | if (tc_type < 0) | |
298 | return -EINVAL; | |
57a4274c | 299 | |
ea410307 AM |
300 | data->thermocouple_type = tc_type; |
301 | max31856_init(data); | |
302 | break; | |
303 | } | |
57a4274c AM |
304 | default: |
305 | return -EINVAL; | |
306 | } | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
fb55a513 PC |
311 | static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) |
312 | { | |
313 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
314 | struct max31856_data *data = iio_priv(indio_dev); | |
315 | u8 reg_val; | |
316 | int ret; | |
317 | bool fault; | |
318 | ||
319 | ret = max31856_read(data, MAX31856_SR_REG, ®_val, 1); | |
320 | if (ret) | |
321 | return ret; | |
322 | ||
323 | fault = reg_val & faultbit; | |
324 | ||
9df24867 | 325 | return sysfs_emit(buf, "%d\n", fault); |
fb55a513 PC |
326 | } |
327 | ||
328 | static ssize_t show_fault_ovuv(struct device *dev, | |
329 | struct device_attribute *attr, | |
330 | char *buf) | |
331 | { | |
332 | return show_fault(dev, MAX31856_FAULT_OVUV, buf); | |
333 | } | |
334 | ||
335 | static ssize_t show_fault_oc(struct device *dev, | |
336 | struct device_attribute *attr, | |
337 | char *buf) | |
338 | { | |
339 | return show_fault(dev, MAX31856_FAULT_OPEN, buf); | |
340 | } | |
341 | ||
76aa41c1 AM |
342 | static ssize_t show_filter(struct device *dev, |
343 | struct device_attribute *attr, | |
344 | char *buf) | |
345 | { | |
346 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
347 | struct max31856_data *data = iio_priv(indio_dev); | |
348 | ||
9df24867 | 349 | return sysfs_emit(buf, "%d\n", data->filter_50hz ? 50 : 60); |
76aa41c1 AM |
350 | } |
351 | ||
352 | static ssize_t set_filter(struct device *dev, | |
353 | struct device_attribute *attr, | |
354 | const char *buf, | |
355 | size_t len) | |
356 | { | |
357 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
358 | struct max31856_data *data = iio_priv(indio_dev); | |
359 | unsigned int freq; | |
360 | int ret; | |
361 | ||
362 | ret = kstrtouint(buf, 10, &freq); | |
363 | if (ret) | |
364 | return ret; | |
365 | ||
366 | switch (freq) { | |
367 | case 50: | |
368 | data->filter_50hz = true; | |
369 | break; | |
370 | case 60: | |
371 | data->filter_50hz = false; | |
372 | break; | |
373 | default: | |
374 | return -EINVAL; | |
375 | } | |
376 | ||
377 | max31856_init(data); | |
378 | return len; | |
379 | } | |
380 | ||
fb55a513 PC |
381 | static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); |
382 | static IIO_DEVICE_ATTR(fault_oc, 0444, show_fault_oc, NULL, 0); | |
76aa41c1 AM |
383 | static IIO_DEVICE_ATTR(in_temp_filter_notch_center_frequency, 0644, |
384 | show_filter, set_filter, 0); | |
fb55a513 PC |
385 | |
386 | static struct attribute *max31856_attributes[] = { | |
387 | &iio_dev_attr_fault_ovuv.dev_attr.attr, | |
388 | &iio_dev_attr_fault_oc.dev_attr.attr, | |
76aa41c1 | 389 | &iio_dev_attr_in_temp_filter_notch_center_frequency.dev_attr.attr, |
fb55a513 PC |
390 | NULL, |
391 | }; | |
392 | ||
393 | static const struct attribute_group max31856_group = { | |
394 | .attrs = max31856_attributes, | |
395 | }; | |
396 | ||
397 | static const struct iio_info max31856_info = { | |
398 | .read_raw = max31856_read_raw, | |
57a4274c | 399 | .write_raw = max31856_write_raw, |
ea410307 | 400 | .write_raw_get_fmt = max31856_write_raw_get_fmt, |
fb55a513 PC |
401 | .attrs = &max31856_group, |
402 | }; | |
403 | ||
404 | static int max31856_probe(struct spi_device *spi) | |
405 | { | |
406 | const struct spi_device_id *id = spi_get_device_id(spi); | |
407 | struct iio_dev *indio_dev; | |
408 | struct max31856_data *data; | |
409 | int ret; | |
410 | ||
411 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); | |
412 | if (!indio_dev) | |
413 | return -ENOMEM; | |
414 | ||
415 | data = iio_priv(indio_dev); | |
416 | data->spi = spi; | |
76aa41c1 | 417 | data->filter_50hz = false; |
fb55a513 PC |
418 | |
419 | spi_set_drvdata(spi, indio_dev); | |
420 | ||
421 | indio_dev->info = &max31856_info; | |
422 | indio_dev->name = id->name; | |
423 | indio_dev->modes = INDIO_DIRECT_MODE; | |
424 | indio_dev->channels = max31856_channels; | |
425 | indio_dev->num_channels = ARRAY_SIZE(max31856_channels); | |
426 | ||
60a0548f | 427 | ret = device_property_read_u32(&spi->dev, "thermocouple-type", &data->thermocouple_type); |
fb55a513 PC |
428 | if (ret) { |
429 | dev_info(&spi->dev, | |
430 | "Could not read thermocouple type DT property, configuring as a K-Type\n"); | |
431 | data->thermocouple_type = THERMOCOUPLE_TYPE_K; | |
432 | } | |
433 | ||
434 | /* | |
435 | * no need to translate values as the supported types | |
436 | * have the same value as the #defines | |
437 | */ | |
438 | switch (data->thermocouple_type) { | |
439 | case THERMOCOUPLE_TYPE_B: | |
440 | case THERMOCOUPLE_TYPE_E: | |
441 | case THERMOCOUPLE_TYPE_J: | |
442 | case THERMOCOUPLE_TYPE_K: | |
443 | case THERMOCOUPLE_TYPE_N: | |
444 | case THERMOCOUPLE_TYPE_R: | |
445 | case THERMOCOUPLE_TYPE_S: | |
446 | case THERMOCOUPLE_TYPE_T: | |
447 | break; | |
448 | default: | |
449 | dev_err(&spi->dev, | |
450 | "error: thermocouple-type %u not supported by max31856\n" | |
451 | , data->thermocouple_type); | |
452 | return -EINVAL; | |
453 | } | |
454 | ||
455 | ret = max31856_init(data); | |
456 | if (ret) { | |
457 | dev_err(&spi->dev, "error: Failed to configure max31856\n"); | |
458 | return ret; | |
459 | } | |
460 | ||
461 | return devm_iio_device_register(&spi->dev, indio_dev); | |
462 | } | |
463 | ||
464 | static const struct spi_device_id max31856_id[] = { | |
465 | { "max31856", 0 }, | |
466 | { } | |
467 | }; | |
468 | MODULE_DEVICE_TABLE(spi, max31856_id); | |
469 | ||
470 | static const struct of_device_id max31856_of_match[] = { | |
471 | { .compatible = "maxim,max31856" }, | |
472 | { } | |
473 | }; | |
474 | MODULE_DEVICE_TABLE(of, max31856_of_match); | |
475 | ||
476 | static struct spi_driver max31856_driver = { | |
477 | .driver = { | |
478 | .name = "max31856", | |
479 | .of_match_table = max31856_of_match, | |
480 | }, | |
481 | .probe = max31856_probe, | |
482 | .id_table = max31856_id, | |
483 | }; | |
484 | module_spi_driver(max31856_driver); | |
485 | ||
486 | MODULE_AUTHOR("Paresh Chaudhary <paresh.chaudhary@rockwellcollins.com>"); | |
487 | MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>"); | |
488 | MODULE_DESCRIPTION("Maxim MAX31856 thermocouple sensor driver"); | |
489 | MODULE_LICENSE("GPL"); |