Commit e112dc4e authored by Navin Sankar Velliangiri's avatar Navin Sankar Velliangiri Committed by Jonathan Cameron

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.pdfSigned-off-by: default avatarNavin Sankar Velliangiri <navin@linumiz.com>
Link: https://lore.kernel.org/r/20210824050123.71289-1-navin@linumiz.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent b0fc3f1d
What: /sys/bus/iio/devices/iio:deviceX/fault_ovuv
KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org
Description:
Overvoltage or Undervoltage Input fault. The internal circuitry
is protected from excessive voltages applied to the thermocouple
cables at FORCE+, FORCE2, RTDIN+ & RTDIN-. This circuitry turn
off when the input voltage is negative or greater than VDD.
Reading returns '1' if input voltage is negative or greater
than VDD, otherwise '0'.
What: /sys/bus/iio/devices/iio:deviceX/in_filter_notch_center_frequency
KernelVersion: 5.11
Contact: linux-iio@vger.kernel.org
Description:
Notch frequency in Hz for a noise rejection filter. Used i.e for
line noise rejection.
Valid notch filter values are 50 Hz and 60 Hz.
......@@ -138,4 +138,14 @@ config MAX31856
This driver can also be built as a module. If so, the module
will be called max31856.
config MAX31865
tristate "MAX31865 RTD to Digital converter"
depends on SPI
help
If you say yes here you get support for MAX31865
thermocouple sensor chip connected via SPI.
This driver can also be build as a module. If so, the module
will be called max31865.
endmenu
......@@ -8,6 +8,7 @@ obj-$(CONFIG_LTC2983) += ltc2983.o
obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
obj-$(CONFIG_MAX31856) += max31856.o
obj-$(CONFIG_MAX31865) += max31865.o
obj-$(CONFIG_MLX90614) += mlx90614.o
obj-$(CONFIG_MLX90632) += mlx90632.o
obj-$(CONFIG_TMP006) += tmp006.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) Linumiz 2021
*
* max31865.c - Maxim MAX31865 RTD-to-Digital Converter sensor driver
*
* Author: Navin Sankar Velliangiri <navin@linumiz.com>
*/
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/spi/spi.h>
#include <asm/unaligned.h>
/*
* The MSB of the register value determines whether the following byte will
* be written or read. If it is 0, read will follow and if it is 1, write
* will follow.
*/
#define MAX31865_RD_WR_BIT BIT(7)
#define MAX31865_CFG_VBIAS BIT(7)
#define MAX31865_CFG_1SHOT BIT(5)
#define MAX31865_3WIRE_RTD BIT(4)
#define MAX31865_FAULT_STATUS_CLEAR BIT(1)
#define MAX31865_FILTER_50HZ BIT(0)
/* The MAX31865 registers */
#define MAX31865_CFG_REG 0x00
#define MAX31865_RTD_MSB 0x01
#define MAX31865_FAULT_STATUS 0x07
#define MAX31865_FAULT_OVUV BIT(2)
static const char max31865_show_samp_freq[] = "50 60";
static const struct iio_chan_spec max31865_channels[] = {
{ /* RTD Temperature */
.type = IIO_TEMP,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
},
};
struct max31865_data {
struct spi_device *spi;
struct mutex lock;
bool filter_50hz;
bool three_wire;
u8 buf[2] ____cacheline_aligned;
};
static int max31865_read(struct max31865_data *data, u8 reg,
unsigned int read_size)
{
return spi_write_then_read(data->spi, &reg, 1, data->buf, read_size);
}
static int max31865_write(struct max31865_data *data, size_t len)
{
return spi_write(data->spi, data->buf, len);
}
static int enable_bias(struct max31865_data *data)
{
u8 cfg;
int ret;
ret = max31865_read(data, MAX31865_CFG_REG, 1);
if (ret)
return ret;
cfg = data->buf[0];
data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT;
data->buf[1] = cfg | MAX31865_CFG_VBIAS;
return max31865_write(data, 2);
}
static int disable_bias(struct max31865_data *data)
{
u8 cfg;
int ret;
ret = max31865_read(data, MAX31865_CFG_REG, 1);
if (ret)
return ret;
cfg = data->buf[0];
cfg &= ~MAX31865_CFG_VBIAS;
data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT;
data->buf[1] = cfg;
return max31865_write(data, 2);
}
static int max31865_rtd_read(struct max31865_data *data, int *val)
{
u8 reg;
int ret;
/* Enable BIAS to start the conversion */
ret = enable_bias(data);
if (ret)
return ret;
/* wait 10.5ms before initiating the conversion */
msleep(11);
ret = max31865_read(data, MAX31865_CFG_REG, 1);
if (ret)
return ret;
reg = data->buf[0];
reg |= MAX31865_CFG_1SHOT | MAX31865_FAULT_STATUS_CLEAR;
data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT;
data->buf[1] = reg;
ret = max31865_write(data, 2);
if (ret)
return ret;
if (data->filter_50hz) {
/* 50Hz filter mode requires 62.5ms to complete */
msleep(63);
} else {
/* 60Hz filter mode requires 52ms to complete */
msleep(52);
}
ret = max31865_read(data, MAX31865_RTD_MSB, 2);
if (ret)
return ret;
*val = get_unaligned_be16(&data->buf) >> 1;
return disable_bias(data);
}
static int max31865_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max31865_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
ret = max31865_rtd_read(data, val);
mutex_unlock(&data->lock);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/* Temp. Data resolution is 0.03125 degree centigrade */
*val = 31;
*val2 = 250000; /* 1000 * 0.03125 */
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int max31865_init(struct max31865_data *data)
{
u8 cfg;
int ret;
ret = max31865_read(data, MAX31865_CFG_REG, 1);
if (ret)
return ret;
cfg = data->buf[0];
if (data->three_wire)
/* 3-wire RTD connection */
cfg |= MAX31865_3WIRE_RTD;
if (data->filter_50hz)
/* 50Hz noise rejection filter */
cfg |= MAX31865_FILTER_50HZ;
data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT;
data->buf[1] = cfg;
return max31865_write(data, 2);
}
static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf)
{
int ret;
bool fault;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct max31865_data *data = iio_priv(indio_dev);
ret = max31865_read(data, MAX31865_FAULT_STATUS, 1);
if (ret)
return ret;
fault = data->buf[0] & faultbit;
return sprintf(buf, "%d\n", fault);
}
static ssize_t show_fault_ovuv(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return show_fault(dev, MAX31865_FAULT_OVUV, buf);
}
static ssize_t show_filter(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct max31865_data *data = iio_priv(indio_dev);
return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60);
}
static ssize_t set_filter(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct max31865_data *data = iio_priv(indio_dev);
unsigned int freq;
int ret;
ret = kstrtouint(buf, 10, &freq);
if (ret)
return ret;
switch (freq) {
case 50:
data->filter_50hz = true;
break;
case 60:
data->filter_50hz = false;
break;
default:
return -EINVAL;
}
mutex_lock(&data->lock);
ret = max31865_init(data);
mutex_unlock(&data->lock);
if (ret)
return ret;
return len;
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(max31865_show_samp_freq);
static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0);
static IIO_DEVICE_ATTR(in_filter_notch_center_frequency, 0644,
show_filter, set_filter, 0);
static struct attribute *max31865_attributes[] = {
&iio_dev_attr_fault_ovuv.dev_attr.attr,
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_filter_notch_center_frequency.dev_attr.attr,
NULL,
};
static const struct attribute_group max31865_group = {
.attrs = max31865_attributes,
};
static const struct iio_info max31865_info = {
.read_raw = max31865_read_raw,
.attrs = &max31865_group,
};
static int max31865_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct iio_dev *indio_dev;
struct max31865_data *data;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->spi = spi;
data->filter_50hz = false;
mutex_init(&data->lock);
indio_dev->info = &max31865_info;
indio_dev->name = id->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = max31865_channels;
indio_dev->num_channels = ARRAY_SIZE(max31865_channels);
if (of_property_read_bool(spi->dev.of_node, "maxim,3-wire")) {
/* select 3 wire */
data->three_wire = 1;
} else {
/* select 2 or 4 wire */
data->three_wire = 0;
}
ret = max31865_init(data);
if (ret) {
dev_err(&spi->dev, "error: Failed to configure max31865\n");
return ret;
}
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id max31865_id[] = {
{ "max31865", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, max31865_id);
static const struct of_device_id max31865_of_match[] = {
{ .compatible = "maxim,max31865" },
{ }
};
MODULE_DEVICE_TABLE(of, max31865_of_match);
static struct spi_driver max31865_driver = {
.driver = {
.name = "max31865",
.of_match_table = max31865_of_match,
},
.probe = max31865_probe,
.id_table = max31865_id,
};
module_spi_driver(max31865_driver);
MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>");
MODULE_DESCRIPTION("Maxim MAX31865 RTD-to-Digital Converter sensor driver");
MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment