]>
Commit | Line | Data |
---|---|---|
e112dc4e NSV |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | ||
3 | /* | |
4 | * Copyright (c) Linumiz 2021 | |
5 | * | |
6 | * max31865.c - Maxim MAX31865 RTD-to-Digital Converter sensor driver | |
7 | * | |
8 | * Author: Navin Sankar Velliangiri <[email protected]> | |
9 | */ | |
10 | ||
11 | #include <linux/ctype.h> | |
12 | #include <linux/delay.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/iio/iio.h> | |
17 | #include <linux/iio/sysfs.h> | |
18 | #include <linux/spi/spi.h> | |
19 | #include <asm/unaligned.h> | |
20 | ||
21 | /* | |
22 | * The MSB of the register value determines whether the following byte will | |
23 | * be written or read. If it is 0, read will follow and if it is 1, write | |
24 | * will follow. | |
25 | */ | |
26 | #define MAX31865_RD_WR_BIT BIT(7) | |
27 | ||
28 | #define MAX31865_CFG_VBIAS BIT(7) | |
29 | #define MAX31865_CFG_1SHOT BIT(5) | |
30 | #define MAX31865_3WIRE_RTD BIT(4) | |
31 | #define MAX31865_FAULT_STATUS_CLEAR BIT(1) | |
32 | #define MAX31865_FILTER_50HZ BIT(0) | |
33 | ||
34 | /* The MAX31865 registers */ | |
35 | #define MAX31865_CFG_REG 0x00 | |
36 | #define MAX31865_RTD_MSB 0x01 | |
37 | #define MAX31865_FAULT_STATUS 0x07 | |
38 | ||
39 | #define MAX31865_FAULT_OVUV BIT(2) | |
40 | ||
41 | static const char max31865_show_samp_freq[] = "50 60"; | |
42 | ||
43 | static const struct iio_chan_spec max31865_channels[] = { | |
44 | { /* RTD Temperature */ | |
45 | .type = IIO_TEMP, | |
46 | .info_mask_separate = | |
47 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | |
48 | }, | |
49 | }; | |
50 | ||
51 | struct max31865_data { | |
52 | struct spi_device *spi; | |
53 | struct mutex lock; | |
54 | bool filter_50hz; | |
55 | bool three_wire; | |
56 | u8 buf[2] ____cacheline_aligned; | |
57 | }; | |
58 | ||
59 | static int max31865_read(struct max31865_data *data, u8 reg, | |
60 | unsigned int read_size) | |
61 | { | |
62 | return spi_write_then_read(data->spi, ®, 1, data->buf, read_size); | |
63 | } | |
64 | ||
65 | static int max31865_write(struct max31865_data *data, size_t len) | |
66 | { | |
67 | return spi_write(data->spi, data->buf, len); | |
68 | } | |
69 | ||
70 | static int enable_bias(struct max31865_data *data) | |
71 | { | |
72 | u8 cfg; | |
73 | int ret; | |
74 | ||
75 | ret = max31865_read(data, MAX31865_CFG_REG, 1); | |
76 | if (ret) | |
77 | return ret; | |
78 | ||
79 | cfg = data->buf[0]; | |
80 | ||
81 | data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; | |
82 | data->buf[1] = cfg | MAX31865_CFG_VBIAS; | |
83 | ||
84 | return max31865_write(data, 2); | |
85 | } | |
86 | ||
87 | static int disable_bias(struct max31865_data *data) | |
88 | { | |
89 | u8 cfg; | |
90 | int ret; | |
91 | ||
92 | ret = max31865_read(data, MAX31865_CFG_REG, 1); | |
93 | if (ret) | |
94 | return ret; | |
95 | ||
96 | cfg = data->buf[0]; | |
97 | cfg &= ~MAX31865_CFG_VBIAS; | |
98 | ||
99 | data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; | |
100 | data->buf[1] = cfg; | |
101 | ||
102 | return max31865_write(data, 2); | |
103 | } | |
104 | ||
105 | static int max31865_rtd_read(struct max31865_data *data, int *val) | |
106 | { | |
107 | u8 reg; | |
108 | int ret; | |
109 | ||
110 | /* Enable BIAS to start the conversion */ | |
111 | ret = enable_bias(data); | |
112 | if (ret) | |
113 | return ret; | |
114 | ||
115 | /* wait 10.5ms before initiating the conversion */ | |
116 | msleep(11); | |
117 | ||
118 | ret = max31865_read(data, MAX31865_CFG_REG, 1); | |
119 | if (ret) | |
120 | return ret; | |
121 | ||
122 | reg = data->buf[0]; | |
123 | reg |= MAX31865_CFG_1SHOT | MAX31865_FAULT_STATUS_CLEAR; | |
124 | data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; | |
125 | data->buf[1] = reg; | |
126 | ||
127 | ret = max31865_write(data, 2); | |
128 | if (ret) | |
129 | return ret; | |
130 | ||
131 | if (data->filter_50hz) { | |
132 | /* 50Hz filter mode requires 62.5ms to complete */ | |
133 | msleep(63); | |
134 | } else { | |
135 | /* 60Hz filter mode requires 52ms to complete */ | |
136 | msleep(52); | |
137 | } | |
138 | ||
139 | ret = max31865_read(data, MAX31865_RTD_MSB, 2); | |
140 | if (ret) | |
141 | return ret; | |
142 | ||
143 | *val = get_unaligned_be16(&data->buf) >> 1; | |
144 | ||
145 | return disable_bias(data); | |
146 | } | |
147 | ||
148 | static int max31865_read_raw(struct iio_dev *indio_dev, | |
149 | struct iio_chan_spec const *chan, | |
150 | int *val, int *val2, long mask) | |
151 | { | |
152 | struct max31865_data *data = iio_priv(indio_dev); | |
153 | int ret; | |
154 | ||
155 | switch (mask) { | |
156 | case IIO_CHAN_INFO_RAW: | |
157 | mutex_lock(&data->lock); | |
158 | ret = max31865_rtd_read(data, val); | |
159 | mutex_unlock(&data->lock); | |
160 | if (ret) | |
161 | return ret; | |
162 | return IIO_VAL_INT; | |
163 | case IIO_CHAN_INFO_SCALE: | |
164 | /* Temp. Data resolution is 0.03125 degree centigrade */ | |
165 | *val = 31; | |
166 | *val2 = 250000; /* 1000 * 0.03125 */ | |
167 | return IIO_VAL_INT_PLUS_MICRO; | |
168 | default: | |
169 | return -EINVAL; | |
170 | } | |
171 | } | |
172 | ||
173 | static int max31865_init(struct max31865_data *data) | |
174 | { | |
175 | u8 cfg; | |
176 | int ret; | |
177 | ||
178 | ret = max31865_read(data, MAX31865_CFG_REG, 1); | |
179 | if (ret) | |
180 | return ret; | |
181 | ||
182 | cfg = data->buf[0]; | |
183 | ||
184 | if (data->three_wire) | |
185 | /* 3-wire RTD connection */ | |
186 | cfg |= MAX31865_3WIRE_RTD; | |
187 | ||
188 | if (data->filter_50hz) | |
189 | /* 50Hz noise rejection filter */ | |
190 | cfg |= MAX31865_FILTER_50HZ; | |
191 | ||
192 | data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; | |
193 | data->buf[1] = cfg; | |
194 | ||
195 | return max31865_write(data, 2); | |
196 | } | |
197 | ||
198 | static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) | |
199 | { | |
200 | int ret; | |
201 | bool fault; | |
202 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
203 | struct max31865_data *data = iio_priv(indio_dev); | |
204 | ||
205 | ret = max31865_read(data, MAX31865_FAULT_STATUS, 1); | |
206 | if (ret) | |
207 | return ret; | |
208 | ||
209 | fault = data->buf[0] & faultbit; | |
210 | ||
3c1d2fdd | 211 | return sysfs_emit(buf, "%d\n", fault); |
e112dc4e NSV |
212 | } |
213 | ||
214 | static ssize_t show_fault_ovuv(struct device *dev, | |
215 | struct device_attribute *attr, | |
216 | char *buf) | |
217 | { | |
218 | return show_fault(dev, MAX31865_FAULT_OVUV, buf); | |
219 | } | |
220 | ||
221 | static ssize_t show_filter(struct device *dev, | |
222 | struct device_attribute *attr, | |
223 | char *buf) | |
224 | { | |
225 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
226 | struct max31865_data *data = iio_priv(indio_dev); | |
227 | ||
3c1d2fdd | 228 | return sysfs_emit(buf, "%d\n", data->filter_50hz ? 50 : 60); |
e112dc4e NSV |
229 | } |
230 | ||
231 | static ssize_t set_filter(struct device *dev, | |
232 | struct device_attribute *attr, | |
233 | const char *buf, | |
234 | size_t len) | |
235 | { | |
236 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
237 | struct max31865_data *data = iio_priv(indio_dev); | |
238 | unsigned int freq; | |
239 | int ret; | |
240 | ||
241 | ret = kstrtouint(buf, 10, &freq); | |
242 | if (ret) | |
243 | return ret; | |
244 | ||
245 | switch (freq) { | |
246 | case 50: | |
247 | data->filter_50hz = true; | |
248 | break; | |
249 | case 60: | |
250 | data->filter_50hz = false; | |
251 | break; | |
252 | default: | |
253 | return -EINVAL; | |
254 | } | |
255 | ||
256 | mutex_lock(&data->lock); | |
257 | ret = max31865_init(data); | |
258 | mutex_unlock(&data->lock); | |
259 | if (ret) | |
260 | return ret; | |
261 | ||
262 | return len; | |
263 | } | |
264 | ||
265 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(max31865_show_samp_freq); | |
266 | static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); | |
267 | static IIO_DEVICE_ATTR(in_filter_notch_center_frequency, 0644, | |
268 | show_filter, set_filter, 0); | |
269 | ||
270 | static struct attribute *max31865_attributes[] = { | |
271 | &iio_dev_attr_fault_ovuv.dev_attr.attr, | |
272 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | |
273 | &iio_dev_attr_in_filter_notch_center_frequency.dev_attr.attr, | |
274 | NULL, | |
275 | }; | |
276 | ||
277 | static const struct attribute_group max31865_group = { | |
278 | .attrs = max31865_attributes, | |
279 | }; | |
280 | ||
281 | static const struct iio_info max31865_info = { | |
282 | .read_raw = max31865_read_raw, | |
283 | .attrs = &max31865_group, | |
284 | }; | |
285 | ||
286 | static int max31865_probe(struct spi_device *spi) | |
287 | { | |
288 | const struct spi_device_id *id = spi_get_device_id(spi); | |
289 | struct iio_dev *indio_dev; | |
290 | struct max31865_data *data; | |
291 | int ret; | |
292 | ||
293 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); | |
294 | if (!indio_dev) | |
295 | return -ENOMEM; | |
296 | ||
297 | data = iio_priv(indio_dev); | |
298 | data->spi = spi; | |
299 | data->filter_50hz = false; | |
300 | mutex_init(&data->lock); | |
301 | ||
302 | indio_dev->info = &max31865_info; | |
303 | indio_dev->name = id->name; | |
304 | indio_dev->modes = INDIO_DIRECT_MODE; | |
305 | indio_dev->channels = max31865_channels; | |
306 | indio_dev->num_channels = ARRAY_SIZE(max31865_channels); | |
307 | ||
308 | if (of_property_read_bool(spi->dev.of_node, "maxim,3-wire")) { | |
309 | /* select 3 wire */ | |
310 | data->three_wire = 1; | |
311 | } else { | |
312 | /* select 2 or 4 wire */ | |
313 | data->three_wire = 0; | |
314 | } | |
315 | ||
316 | ret = max31865_init(data); | |
317 | if (ret) { | |
318 | dev_err(&spi->dev, "error: Failed to configure max31865\n"); | |
319 | return ret; | |
320 | } | |
321 | ||
322 | return devm_iio_device_register(&spi->dev, indio_dev); | |
323 | } | |
324 | ||
325 | static const struct spi_device_id max31865_id[] = { | |
326 | { "max31865", 0 }, | |
327 | { } | |
328 | }; | |
329 | MODULE_DEVICE_TABLE(spi, max31865_id); | |
330 | ||
331 | static const struct of_device_id max31865_of_match[] = { | |
332 | { .compatible = "maxim,max31865" }, | |
333 | { } | |
334 | }; | |
335 | MODULE_DEVICE_TABLE(of, max31865_of_match); | |
336 | ||
337 | static struct spi_driver max31865_driver = { | |
338 | .driver = { | |
339 | .name = "max31865", | |
340 | .of_match_table = max31865_of_match, | |
341 | }, | |
342 | .probe = max31865_probe, | |
343 | .id_table = max31865_id, | |
344 | }; | |
345 | module_spi_driver(max31865_driver); | |
346 | ||
347 | MODULE_AUTHOR("Navin Sankar Velliangiri <[email protected]>"); | |
348 | MODULE_DESCRIPTION("Maxim MAX31865 RTD-to-Digital Converter sensor driver"); | |
349 | MODULE_LICENSE("GPL v2"); |