From 76aa41c17befa67b8727942d134eb65af501908b Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:48 +0100 Subject: iio: max31856: add option for setting mains filter rejection frequency This sensor has an embedded notch filter for reducing interferences caused by the power mains. This filter can be tuned to reject either 50Hz or 60Hz (and harmonics). Currently the said setting is left alone (the sensor defaults to 60Hz). This patch introduces a IIO attribute that allows the user to set the said filter to the desired frequency. NOTE: this has been intentionally not tied to any DT property to allow the configuration of this setting from userspace, e.g. with a GUI or by reading a configuration file, or maybe reading a GPIO tied to a physical switch or accessing some device that can autodetect the line frequency. Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Reviewed-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/max31856.c | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'drivers/iio/temperature/max31856.c') diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 73ed550e3fc9..d97ba9ee1598 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -23,6 +23,7 @@ #define MAX31856_CR0_1SHOT BIT(6) #define MAX31856_CR0_OCFAULT BIT(4) #define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4) +#define MAX31856_CR0_FILTER_50HZ BIT(0) #define MAX31856_TC_TYPE_MASK GENMASK(3, 0) #define MAX31856_FAULT_OVUV BIT(1) #define MAX31856_FAULT_OPEN BIT(0) @@ -63,6 +64,7 @@ static const struct iio_chan_spec max31856_channels[] = { struct max31856_data { struct spi_device *spi; u32 thermocouple_type; + bool filter_50hz; }; static int max31856_read(struct max31856_data *data, u8 reg, @@ -123,6 +125,11 @@ static int max31856_init(struct max31856_data *data) reg_cr0_val &= ~MAX31856_CR0_1SHOT; reg_cr0_val |= MAX31856_CR0_AUTOCONVERT; + if (data->filter_50hz) + reg_cr0_val |= MAX31856_CR0_FILTER_50HZ; + else + reg_cr0_val &= ~MAX31856_CR0_FILTER_50HZ; + return max31856_write(data, MAX31856_CR0_REG, reg_cr0_val); } @@ -249,12 +256,54 @@ static ssize_t show_fault_oc(struct device *dev, return show_fault(dev, MAX31856_FAULT_OPEN, 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 max31856_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 max31856_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; + } + + max31856_init(data); + return len; +} + static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); static IIO_DEVICE_ATTR(fault_oc, 0444, show_fault_oc, NULL, 0); +static IIO_DEVICE_ATTR(in_temp_filter_notch_center_frequency, 0644, + show_filter, set_filter, 0); static struct attribute *max31856_attributes[] = { &iio_dev_attr_fault_ovuv.dev_attr.attr, &iio_dev_attr_fault_oc.dev_attr.attr, + &iio_dev_attr_in_temp_filter_notch_center_frequency.dev_attr.attr, NULL, }; @@ -280,6 +329,7 @@ static int max31856_probe(struct spi_device *spi) data = iio_priv(indio_dev); data->spi = spi; + data->filter_50hz = false; spi_set_drvdata(spi, indio_dev); -- cgit v1.2.3 From 57a4274ce168d4e63a7b5f4a8a776f7bdd5666a9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:50 +0100 Subject: iio: max31856: add support for configuring the HW averaging This sensor can perform samples averaging in hardware, but currently the driver leaves this setting alone (default is no averaging). This patch binds this HW setting to the "oversampling_ratio" IIO attribute and allows the user to set the averaging as desired (the HW supports averaging of 2, 5, 8 or 16 samples; in-between values are rounded up). Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/max31856.c | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers/iio/temperature/max31856.c') diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index d97ba9ee1598..8457ca9ae326 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /* * The MSB of the register value determines whether the following byte will @@ -24,6 +25,8 @@ #define MAX31856_CR0_OCFAULT BIT(4) #define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4) #define MAX31856_CR0_FILTER_50HZ BIT(0) +#define MAX31856_AVERAGING_MASK GENMASK(6, 4) +#define MAX31856_AVERAGING_SHIFT 4 #define MAX31856_TC_TYPE_MASK GENMASK(3, 0) #define MAX31856_FAULT_OVUV BIT(1) #define MAX31856_FAULT_OPEN BIT(0) @@ -51,6 +54,8 @@ static const struct iio_chan_spec max31856_channels[] = { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) }, { /* Cold Junction Temperature */ .type = IIO_TEMP, @@ -58,6 +63,8 @@ static const struct iio_chan_spec max31856_channels[] = { .modified = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) }, }; @@ -65,6 +72,7 @@ struct max31856_data { struct spi_device *spi; u32 thermocouple_type; bool filter_50hz; + int averaging; }; static int max31856_read(struct max31856_data *data, u8 reg, @@ -109,6 +117,10 @@ static int max31856_init(struct max31856_data *data) reg_cr1_val &= ~MAX31856_TC_TYPE_MASK; reg_cr1_val |= data->thermocouple_type; + + reg_cr1_val &= ~MAX31856_AVERAGING_MASK; + reg_cr1_val |= data->averaging << MAX31856_AVERAGING_SHIFT; + ret = max31856_write(data, MAX31856_CR1_REG, reg_cr1_val); if (ret) return ret; @@ -217,6 +229,9 @@ static int max31856_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; } break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = 1 << data->averaging; + return IIO_VAL_INT; default: ret = -EINVAL; break; @@ -225,6 +240,33 @@ static int max31856_read_raw(struct iio_dev *indio_dev, return ret; } +static int max31856_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max31856_data *data = iio_priv(indio_dev); + int msb; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val > 16 || val < 1) + return -EINVAL; + msb = fls(val) - 1; + /* Round up to next 2pow if needed */ + if (BIT(msb) < val) + msb++; + + data->averaging = msb; + max31856_init(data); + break; + + default: + return -EINVAL; + } + + return 0; +} + static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -313,6 +355,7 @@ static const struct attribute_group max31856_group = { static const struct iio_info max31856_info = { .read_raw = max31856_read_raw, + .write_raw = max31856_write_raw, .attrs = &max31856_group, }; -- cgit v1.2.3 From ea4103070f03f789f305bdd1803d5d4c42f0b3b9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 20 Nov 2019 15:47:54 +0100 Subject: iio: max31856: add support for runtime-configuring the thermocouple type The sensor support various thermocouple types (e.g. J, K, N, ...). The driver allows to configure this parameter using a DT property. This is useful when i.e. the thermocouple is physically tied to the sensor and it is usually not removed, or when it is at least known in advance which sensor will be connected to the circuit. However, if the user can randomly connect any kind of thermocouples (i.e. the device exposes a connector, and the user is free to connect its own sensors), it would be more appropriate to provide a mechanism to dynamically switch from one thermocouple type to another. This can be i.e. handled in userspace by a GUI, a configuration file or a program that detects the thermocouple type by reading a GPIO, or a eeprom on the probe, or whatever. This patch adds a IIO attribute that can be used to override, at run-time, the DT-provided setting (which serves as default). Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Colin Ian King Cc: Patrick Havelange Cc: Matt Weber Cc: Matt Ranostay Cc: Chuhong Yuan Cc: Daniel Gomez Cc: linux-iio@vger.kernel.org Signed-off-by: Andrea Merello Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/max31856.c | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers/iio/temperature/max31856.c') diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c index 8457ca9ae326..b4cb21ab2e85 100644 --- a/drivers/iio/temperature/max31856.c +++ b/drivers/iio/temperature/max31856.c @@ -6,6 +6,7 @@ * Copyright (C) 2018-2019 Rockwell Collins */ +#include #include #include #include @@ -53,7 +54,8 @@ static const struct iio_chan_spec max31856_channels[] = { { /* Thermocouple Temperature */ .type = IIO_TEMP, .info_mask_separate = - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) }, @@ -75,6 +77,10 @@ struct max31856_data { int averaging; }; +static const char max31856_tc_types[] = { + 'B', 'E', 'J', 'K', 'N', 'R', 'S', 'T' +}; + static int max31856_read(struct max31856_data *data, u8 reg, u8 val[], unsigned int read_size) { @@ -232,6 +238,9 @@ static int max31856_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = 1 << data->averaging; return IIO_VAL_INT; + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + *val = max31856_tc_types[data->thermocouple_type]; + return IIO_VAL_CHAR; default: ret = -EINVAL; break; @@ -240,6 +249,18 @@ static int max31856_read_raw(struct iio_dev *indio_dev, return ret; } +static int max31856_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + return IIO_VAL_CHAR; + default: + return IIO_VAL_INT; + } +} + static int max31856_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -259,7 +280,24 @@ static int max31856_write_raw(struct iio_dev *indio_dev, data->averaging = msb; max31856_init(data); break; + case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: + { + int tc_type = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(max31856_tc_types); i++) { + if (max31856_tc_types[i] == toupper(val)) { + tc_type = i; + break; + } + } + if (tc_type < 0) + return -EINVAL; + data->thermocouple_type = tc_type; + max31856_init(data); + break; + } default: return -EINVAL; } @@ -356,6 +394,7 @@ static const struct attribute_group max31856_group = { static const struct iio_info max31856_info = { .read_raw = max31856_read_raw, .write_raw = max31856_write_raw, + .write_raw_get_fmt = max31856_write_raw_get_fmt, .attrs = &max31856_group, }; -- cgit v1.2.3