18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* max31856.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Maxim MAX31856 thermocouple sensor driver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 Rockwell Collins 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 168c2ecf20Sopenharmony_ci#include <linux/util_macros.h> 178c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 188c2ecf20Sopenharmony_ci#include <dt-bindings/iio/temperature/thermocouple.h> 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * The MSB of the register value determines whether the following byte will 218c2ecf20Sopenharmony_ci * be written or read. If it is 0, one or more byte reads will follow. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci#define MAX31856_RD_WR_BIT BIT(7) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MAX31856_CR0_AUTOCONVERT BIT(7) 268c2ecf20Sopenharmony_ci#define MAX31856_CR0_1SHOT BIT(6) 278c2ecf20Sopenharmony_ci#define MAX31856_CR0_OCFAULT BIT(4) 288c2ecf20Sopenharmony_ci#define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4) 298c2ecf20Sopenharmony_ci#define MAX31856_CR0_FILTER_50HZ BIT(0) 308c2ecf20Sopenharmony_ci#define MAX31856_AVERAGING_MASK GENMASK(6, 4) 318c2ecf20Sopenharmony_ci#define MAX31856_AVERAGING_SHIFT 4 328c2ecf20Sopenharmony_ci#define MAX31856_TC_TYPE_MASK GENMASK(3, 0) 338c2ecf20Sopenharmony_ci#define MAX31856_FAULT_OVUV BIT(1) 348c2ecf20Sopenharmony_ci#define MAX31856_FAULT_OPEN BIT(0) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* The MAX31856 registers */ 378c2ecf20Sopenharmony_ci#define MAX31856_CR0_REG 0x00 388c2ecf20Sopenharmony_ci#define MAX31856_CR1_REG 0x01 398c2ecf20Sopenharmony_ci#define MAX31856_MASK_REG 0x02 408c2ecf20Sopenharmony_ci#define MAX31856_CJHF_REG 0x03 418c2ecf20Sopenharmony_ci#define MAX31856_CJLF_REG 0x04 428c2ecf20Sopenharmony_ci#define MAX31856_LTHFTH_REG 0x05 438c2ecf20Sopenharmony_ci#define MAX31856_LTHFTL_REG 0x06 448c2ecf20Sopenharmony_ci#define MAX31856_LTLFTH_REG 0x07 458c2ecf20Sopenharmony_ci#define MAX31856_LTLFTL_REG 0x08 468c2ecf20Sopenharmony_ci#define MAX31856_CJTO_REG 0x09 478c2ecf20Sopenharmony_ci#define MAX31856_CJTH_REG 0x0A 488c2ecf20Sopenharmony_ci#define MAX31856_CJTL_REG 0x0B 498c2ecf20Sopenharmony_ci#define MAX31856_LTCBH_REG 0x0C 508c2ecf20Sopenharmony_ci#define MAX31856_LTCBM_REG 0x0D 518c2ecf20Sopenharmony_ci#define MAX31856_LTCBL_REG 0x0E 528c2ecf20Sopenharmony_ci#define MAX31856_SR_REG 0x0F 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct iio_chan_spec max31856_channels[] = { 558c2ecf20Sopenharmony_ci { /* Thermocouple Temperature */ 568c2ecf20Sopenharmony_ci .type = IIO_TEMP, 578c2ecf20Sopenharmony_ci .info_mask_separate = 588c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | 598c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), 608c2ecf20Sopenharmony_ci .info_mask_shared_by_type = 618c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) 628c2ecf20Sopenharmony_ci }, 638c2ecf20Sopenharmony_ci { /* Cold Junction Temperature */ 648c2ecf20Sopenharmony_ci .type = IIO_TEMP, 658c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_TEMP_AMBIENT, 668c2ecf20Sopenharmony_ci .modified = 1, 678c2ecf20Sopenharmony_ci .info_mask_separate = 688c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), 698c2ecf20Sopenharmony_ci .info_mask_shared_by_type = 708c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) 718c2ecf20Sopenharmony_ci }, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct max31856_data { 758c2ecf20Sopenharmony_ci struct spi_device *spi; 768c2ecf20Sopenharmony_ci u32 thermocouple_type; 778c2ecf20Sopenharmony_ci bool filter_50hz; 788c2ecf20Sopenharmony_ci int averaging; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic const char max31856_tc_types[] = { 828c2ecf20Sopenharmony_ci 'B', 'E', 'J', 'K', 'N', 'R', 'S', 'T' 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int max31856_read(struct max31856_data *data, u8 reg, 868c2ecf20Sopenharmony_ci u8 val[], unsigned int read_size) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return spi_write_then_read(data->spi, ®, 1, val, read_size); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int max31856_write(struct max31856_data *data, u8 reg, 928c2ecf20Sopenharmony_ci unsigned int val) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u8 buf[2]; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci buf[0] = reg | (MAX31856_RD_WR_BIT); 978c2ecf20Sopenharmony_ci buf[1] = val; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return spi_write(data->spi, buf, 2); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int max31856_init(struct max31856_data *data) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci u8 reg_cr0_val, reg_cr1_val; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Start by changing to Off mode before making changes as 1088c2ecf20Sopenharmony_ci * some settings are recommended to be set only when the device 1098c2ecf20Sopenharmony_ci * is off 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci ret = max31856_read(data, MAX31856_CR0_REG, ®_cr0_val, 1); 1128c2ecf20Sopenharmony_ci if (ret) 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci reg_cr0_val &= ~MAX31856_CR0_AUTOCONVERT; 1168c2ecf20Sopenharmony_ci ret = max31856_write(data, MAX31856_CR0_REG, reg_cr0_val); 1178c2ecf20Sopenharmony_ci if (ret) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Set thermocouple type based on dts property */ 1218c2ecf20Sopenharmony_ci ret = max31856_read(data, MAX31856_CR1_REG, ®_cr1_val, 1); 1228c2ecf20Sopenharmony_ci if (ret) 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci reg_cr1_val &= ~MAX31856_TC_TYPE_MASK; 1268c2ecf20Sopenharmony_ci reg_cr1_val |= data->thermocouple_type; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci reg_cr1_val &= ~MAX31856_AVERAGING_MASK; 1298c2ecf20Sopenharmony_ci reg_cr1_val |= data->averaging << MAX31856_AVERAGING_SHIFT; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = max31856_write(data, MAX31856_CR1_REG, reg_cr1_val); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * Enable Open circuit fault detection 1378c2ecf20Sopenharmony_ci * Read datasheet for more information: Table 4. 1388c2ecf20Sopenharmony_ci * Value 01 means : Enabled (Once every 16 conversions) 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci reg_cr0_val &= ~MAX31856_CR0_OCFAULT_MASK; 1418c2ecf20Sopenharmony_ci reg_cr0_val |= MAX31856_CR0_OCFAULT; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Set Auto Conversion Mode */ 1448c2ecf20Sopenharmony_ci reg_cr0_val &= ~MAX31856_CR0_1SHOT; 1458c2ecf20Sopenharmony_ci reg_cr0_val |= MAX31856_CR0_AUTOCONVERT; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (data->filter_50hz) 1488c2ecf20Sopenharmony_ci reg_cr0_val |= MAX31856_CR0_FILTER_50HZ; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci reg_cr0_val &= ~MAX31856_CR0_FILTER_50HZ; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return max31856_write(data, MAX31856_CR0_REG, reg_cr0_val); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int max31856_thermocouple_read(struct max31856_data *data, 1568c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1578c2ecf20Sopenharmony_ci int *val) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int ret, offset_cjto; 1608c2ecf20Sopenharmony_ci u8 reg_val[3]; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci switch (chan->channel2) { 1638c2ecf20Sopenharmony_ci case IIO_NO_MOD: 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * Multibyte Read 1668c2ecf20Sopenharmony_ci * MAX31856_LTCBH_REG, MAX31856_LTCBM_REG, MAX31856_LTCBL_REG 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci ret = max31856_read(data, MAX31856_LTCBH_REG, reg_val, 3); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci /* Skip last 5 dead bits of LTCBL */ 1728c2ecf20Sopenharmony_ci *val = get_unaligned_be24(®_val[0]) >> 5; 1738c2ecf20Sopenharmony_ci /* Check 7th bit of LTCBH reg. value for sign*/ 1748c2ecf20Sopenharmony_ci if (reg_val[0] & 0x80) 1758c2ecf20Sopenharmony_ci *val -= 0x80000; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * Multibyte Read 1818c2ecf20Sopenharmony_ci * MAX31856_CJTO_REG, MAX31856_CJTH_REG, MAX31856_CJTL_REG 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci ret = max31856_read(data, MAX31856_CJTO_REG, reg_val, 3); 1848c2ecf20Sopenharmony_ci if (ret) 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci /* Get Cold Junction Temp. offset register value */ 1878c2ecf20Sopenharmony_ci offset_cjto = reg_val[0]; 1888c2ecf20Sopenharmony_ci /* Get CJTH and CJTL value and skip last 2 dead bits of CJTL */ 1898c2ecf20Sopenharmony_ci *val = get_unaligned_be16(®_val[1]) >> 2; 1908c2ecf20Sopenharmony_ci /* As per datasheet add offset into CJTH and CJTL */ 1918c2ecf20Sopenharmony_ci *val += offset_cjto; 1928c2ecf20Sopenharmony_ci /* Check 7th bit of CJTH reg. value for sign */ 1938c2ecf20Sopenharmony_ci if (reg_val[1] & 0x80) 1948c2ecf20Sopenharmony_ci *val -= 0x4000; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci default: 1988c2ecf20Sopenharmony_ci return -EINVAL; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ret = max31856_read(data, MAX31856_SR_REG, reg_val, 1); 2028c2ecf20Sopenharmony_ci if (ret) 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci /* Check for over/under voltage or open circuit fault */ 2058c2ecf20Sopenharmony_ci if (reg_val[0] & (MAX31856_FAULT_OVUV | MAX31856_FAULT_OPEN)) 2068c2ecf20Sopenharmony_ci return -EIO; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int max31856_read_raw(struct iio_dev *indio_dev, 2128c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2138c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct max31856_data *data = iio_priv(indio_dev); 2168c2ecf20Sopenharmony_ci int ret; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci switch (mask) { 2198c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 2208c2ecf20Sopenharmony_ci ret = max31856_thermocouple_read(data, chan, val); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2248c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 2258c2ecf20Sopenharmony_ci switch (chan->channel2) { 2268c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: 2278c2ecf20Sopenharmony_ci /* Cold junction Temp. Data resolution is 0.015625 */ 2288c2ecf20Sopenharmony_ci *val = 15; 2298c2ecf20Sopenharmony_ci *val2 = 625000; /* 1000 * 0.015625 */ 2308c2ecf20Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci default: 2338c2ecf20Sopenharmony_ci /* Thermocouple Temp. Data resolution is 0.0078125 */ 2348c2ecf20Sopenharmony_ci *val = 7; 2358c2ecf20Sopenharmony_ci *val2 = 812500; /* 1000 * 0.0078125) */ 2368c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 2408c2ecf20Sopenharmony_ci *val = 1 << data->averaging; 2418c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2428c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: 2438c2ecf20Sopenharmony_ci *val = max31856_tc_types[data->thermocouple_type]; 2448c2ecf20Sopenharmony_ci return IIO_VAL_CHAR; 2458c2ecf20Sopenharmony_ci default: 2468c2ecf20Sopenharmony_ci ret = -EINVAL; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return ret; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int max31856_write_raw_get_fmt(struct iio_dev *indio_dev, 2548c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2558c2ecf20Sopenharmony_ci long mask) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci switch (mask) { 2588c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: 2598c2ecf20Sopenharmony_ci return IIO_VAL_CHAR; 2608c2ecf20Sopenharmony_ci default: 2618c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int max31856_write_raw(struct iio_dev *indio_dev, 2668c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2678c2ecf20Sopenharmony_ci int val, int val2, long mask) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct max31856_data *data = iio_priv(indio_dev); 2708c2ecf20Sopenharmony_ci int msb; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci switch (mask) { 2738c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 2748c2ecf20Sopenharmony_ci if (val > 16 || val < 1) 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci msb = fls(val) - 1; 2778c2ecf20Sopenharmony_ci /* Round up to next 2pow if needed */ 2788c2ecf20Sopenharmony_ci if (BIT(msb) < val) 2798c2ecf20Sopenharmony_ci msb++; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci data->averaging = msb; 2828c2ecf20Sopenharmony_ci max31856_init(data); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: 2858c2ecf20Sopenharmony_ci { 2868c2ecf20Sopenharmony_ci int tc_type = -1; 2878c2ecf20Sopenharmony_ci int i; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(max31856_tc_types); i++) { 2908c2ecf20Sopenharmony_ci if (max31856_tc_types[i] == toupper(val)) { 2918c2ecf20Sopenharmony_ci tc_type = i; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci if (tc_type < 0) 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci data->thermocouple_type = tc_type; 2998c2ecf20Sopenharmony_ci max31856_init(data); 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3128c2ecf20Sopenharmony_ci struct max31856_data *data = iio_priv(indio_dev); 3138c2ecf20Sopenharmony_ci u8 reg_val; 3148c2ecf20Sopenharmony_ci int ret; 3158c2ecf20Sopenharmony_ci bool fault; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = max31856_read(data, MAX31856_SR_REG, ®_val, 1); 3188c2ecf20Sopenharmony_ci if (ret) 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci fault = reg_val & faultbit; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fault); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic ssize_t show_fault_ovuv(struct device *dev, 3278c2ecf20Sopenharmony_ci struct device_attribute *attr, 3288c2ecf20Sopenharmony_ci char *buf) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci return show_fault(dev, MAX31856_FAULT_OVUV, buf); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic ssize_t show_fault_oc(struct device *dev, 3348c2ecf20Sopenharmony_ci struct device_attribute *attr, 3358c2ecf20Sopenharmony_ci char *buf) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci return show_fault(dev, MAX31856_FAULT_OPEN, buf); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic ssize_t show_filter(struct device *dev, 3418c2ecf20Sopenharmony_ci struct device_attribute *attr, 3428c2ecf20Sopenharmony_ci char *buf) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3458c2ecf20Sopenharmony_ci struct max31856_data *data = iio_priv(indio_dev); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic ssize_t set_filter(struct device *dev, 3518c2ecf20Sopenharmony_ci struct device_attribute *attr, 3528c2ecf20Sopenharmony_ci const char *buf, 3538c2ecf20Sopenharmony_ci size_t len) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 3568c2ecf20Sopenharmony_ci struct max31856_data *data = iio_priv(indio_dev); 3578c2ecf20Sopenharmony_ci unsigned int freq; 3588c2ecf20Sopenharmony_ci int ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = kstrtouint(buf, 10, &freq); 3618c2ecf20Sopenharmony_ci if (ret) 3628c2ecf20Sopenharmony_ci return ret; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci switch (freq) { 3658c2ecf20Sopenharmony_ci case 50: 3668c2ecf20Sopenharmony_ci data->filter_50hz = true; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case 60: 3698c2ecf20Sopenharmony_ci data->filter_50hz = false; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci default: 3728c2ecf20Sopenharmony_ci return -EINVAL; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci max31856_init(data); 3768c2ecf20Sopenharmony_ci return len; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); 3808c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(fault_oc, 0444, show_fault_oc, NULL, 0); 3818c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(in_temp_filter_notch_center_frequency, 0644, 3828c2ecf20Sopenharmony_ci show_filter, set_filter, 0); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic struct attribute *max31856_attributes[] = { 3858c2ecf20Sopenharmony_ci &iio_dev_attr_fault_ovuv.dev_attr.attr, 3868c2ecf20Sopenharmony_ci &iio_dev_attr_fault_oc.dev_attr.attr, 3878c2ecf20Sopenharmony_ci &iio_dev_attr_in_temp_filter_notch_center_frequency.dev_attr.attr, 3888c2ecf20Sopenharmony_ci NULL, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic const struct attribute_group max31856_group = { 3928c2ecf20Sopenharmony_ci .attrs = max31856_attributes, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct iio_info max31856_info = { 3968c2ecf20Sopenharmony_ci .read_raw = max31856_read_raw, 3978c2ecf20Sopenharmony_ci .write_raw = max31856_write_raw, 3988c2ecf20Sopenharmony_ci .write_raw_get_fmt = max31856_write_raw_get_fmt, 3998c2ecf20Sopenharmony_ci .attrs = &max31856_group, 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int max31856_probe(struct spi_device *spi) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci const struct spi_device_id *id = spi_get_device_id(spi); 4058c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 4068c2ecf20Sopenharmony_ci struct max31856_data *data; 4078c2ecf20Sopenharmony_ci int ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); 4108c2ecf20Sopenharmony_ci if (!indio_dev) 4118c2ecf20Sopenharmony_ci return -ENOMEM; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 4148c2ecf20Sopenharmony_ci data->spi = spi; 4158c2ecf20Sopenharmony_ci data->filter_50hz = false; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci spi_set_drvdata(spi, indio_dev); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci indio_dev->info = &max31856_info; 4208c2ecf20Sopenharmony_ci indio_dev->name = id->name; 4218c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 4228c2ecf20Sopenharmony_ci indio_dev->channels = max31856_channels; 4238c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(max31856_channels); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ret = of_property_read_u32(spi->dev.of_node, "thermocouple-type", 4268c2ecf20Sopenharmony_ci &data->thermocouple_type); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (ret) { 4298c2ecf20Sopenharmony_ci dev_info(&spi->dev, 4308c2ecf20Sopenharmony_ci "Could not read thermocouple type DT property, configuring as a K-Type\n"); 4318c2ecf20Sopenharmony_ci data->thermocouple_type = THERMOCOUPLE_TYPE_K; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * no need to translate values as the supported types 4368c2ecf20Sopenharmony_ci * have the same value as the #defines 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci switch (data->thermocouple_type) { 4398c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_B: 4408c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_E: 4418c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_J: 4428c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_K: 4438c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_N: 4448c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_R: 4458c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_S: 4468c2ecf20Sopenharmony_ci case THERMOCOUPLE_TYPE_T: 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci default: 4498c2ecf20Sopenharmony_ci dev_err(&spi->dev, 4508c2ecf20Sopenharmony_ci "error: thermocouple-type %u not supported by max31856\n" 4518c2ecf20Sopenharmony_ci , data->thermocouple_type); 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ret = max31856_init(data); 4568c2ecf20Sopenharmony_ci if (ret) { 4578c2ecf20Sopenharmony_ci dev_err(&spi->dev, "error: Failed to configure max31856\n"); 4588c2ecf20Sopenharmony_ci return ret; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return devm_iio_device_register(&spi->dev, indio_dev); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic const struct spi_device_id max31856_id[] = { 4658c2ecf20Sopenharmony_ci { "max31856", 0 }, 4668c2ecf20Sopenharmony_ci { } 4678c2ecf20Sopenharmony_ci}; 4688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, max31856_id); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic const struct of_device_id max31856_of_match[] = { 4718c2ecf20Sopenharmony_ci { .compatible = "maxim,max31856" }, 4728c2ecf20Sopenharmony_ci { } 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max31856_of_match); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic struct spi_driver max31856_driver = { 4778c2ecf20Sopenharmony_ci .driver = { 4788c2ecf20Sopenharmony_ci .name = "max31856", 4798c2ecf20Sopenharmony_ci .of_match_table = max31856_of_match, 4808c2ecf20Sopenharmony_ci }, 4818c2ecf20Sopenharmony_ci .probe = max31856_probe, 4828c2ecf20Sopenharmony_ci .id_table = max31856_id, 4838c2ecf20Sopenharmony_ci}; 4848c2ecf20Sopenharmony_cimodule_spi_driver(max31856_driver); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paresh Chaudhary <paresh.chaudhary@rockwellcollins.com>"); 4878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>"); 4888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX31856 thermocouple sensor driver"); 4898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 490