Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: temperature: Add MAX31865 RTD Support

This patch adds support for Maxim MAX31865 RTD temperature
sensor support.

More information can be found in:
https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf

Signed-off-by: Navin Sankar Velliangiri <navin@linumiz.com>
Link: https://lore.kernel.org/r/20210824050123.71289-1-navin@linumiz.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Navin Sankar Velliangiri and committed by
Jonathan Cameron
e112dc4e b0fc3f1d

+380
+20
Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865
··· 1 + What: /sys/bus/iio/devices/iio:deviceX/fault_ovuv 2 + KernelVersion: 5.11 3 + Contact: linux-iio@vger.kernel.org 4 + Description: 5 + Overvoltage or Undervoltage Input fault. The internal circuitry 6 + is protected from excessive voltages applied to the thermocouple 7 + cables at FORCE+, FORCE2, RTDIN+ & RTDIN-. This circuitry turn 8 + off when the input voltage is negative or greater than VDD. 9 + 10 + Reading returns '1' if input voltage is negative or greater 11 + than VDD, otherwise '0'. 12 + 13 + What: /sys/bus/iio/devices/iio:deviceX/in_filter_notch_center_frequency 14 + KernelVersion: 5.11 15 + Contact: linux-iio@vger.kernel.org 16 + Description: 17 + Notch frequency in Hz for a noise rejection filter. Used i.e for 18 + line noise rejection. 19 + 20 + Valid notch filter values are 50 Hz and 60 Hz.
+10
drivers/iio/temperature/Kconfig
··· 138 138 This driver can also be built as a module. If so, the module 139 139 will be called max31856. 140 140 141 + config MAX31865 142 + tristate "MAX31865 RTD to Digital converter" 143 + depends on SPI 144 + help 145 + If you say yes here you get support for MAX31865 146 + thermocouple sensor chip connected via SPI. 147 + 148 + This driver can also be build as a module. If so, the module 149 + will be called max31865. 150 + 141 151 endmenu
+1
drivers/iio/temperature/Makefile
··· 8 8 obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o 9 9 obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o 10 10 obj-$(CONFIG_MAX31856) += max31856.o 11 + obj-$(CONFIG_MAX31865) += max31865.o 11 12 obj-$(CONFIG_MLX90614) += mlx90614.o 12 13 obj-$(CONFIG_MLX90632) += mlx90632.o 13 14 obj-$(CONFIG_TMP006) += tmp006.o
+349
drivers/iio/temperature/max31865.c
··· 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 <navin@linumiz.com> 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, &reg, 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 + 211 + return sprintf(buf, "%d\n", fault); 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 + 228 + return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); 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 <navin@linumiz.com>"); 348 + MODULE_DESCRIPTION("Maxim MAX31865 RTD-to-Digital Converter sensor driver"); 349 + MODULE_LICENSE("GPL v2");