18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * maxim_thermocouple.c - Support for Maxim thermocouple chips 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 Matt Ranostay 68c2ecf20Sopenharmony_ci * Author: <matt.ranostay@konsulko.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 168c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 178c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 188c2ecf20Sopenharmony_ci#include <linux/iio/trigger.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cienum { 268c2ecf20Sopenharmony_ci MAX6675, 278c2ecf20Sopenharmony_ci MAX31855, 288c2ecf20Sopenharmony_ci MAX31855K, 298c2ecf20Sopenharmony_ci MAX31855J, 308c2ecf20Sopenharmony_ci MAX31855N, 318c2ecf20Sopenharmony_ci MAX31855S, 328c2ecf20Sopenharmony_ci MAX31855T, 338c2ecf20Sopenharmony_ci MAX31855E, 348c2ecf20Sopenharmony_ci MAX31855R, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const char maxim_tc_types[] = { 388c2ecf20Sopenharmony_ci 'K', '?', 'K', 'J', 'N', 'S', 'T', 'E', 'R' 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct iio_chan_spec max6675_channels[] = { 428c2ecf20Sopenharmony_ci { /* thermocouple temperature */ 438c2ecf20Sopenharmony_ci .type = IIO_TEMP, 448c2ecf20Sopenharmony_ci .info_mask_separate = 458c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | 468c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), 478c2ecf20Sopenharmony_ci .scan_index = 0, 488c2ecf20Sopenharmony_ci .scan_type = { 498c2ecf20Sopenharmony_ci .sign = 's', 508c2ecf20Sopenharmony_ci .realbits = 13, 518c2ecf20Sopenharmony_ci .storagebits = 16, 528c2ecf20Sopenharmony_ci .shift = 3, 538c2ecf20Sopenharmony_ci .endianness = IIO_BE, 548c2ecf20Sopenharmony_ci }, 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(1), 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct iio_chan_spec max31855_channels[] = { 608c2ecf20Sopenharmony_ci { /* thermocouple temperature */ 618c2ecf20Sopenharmony_ci .type = IIO_TEMP, 628c2ecf20Sopenharmony_ci .address = 2, 638c2ecf20Sopenharmony_ci .info_mask_separate = 648c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | 658c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE), 668c2ecf20Sopenharmony_ci .scan_index = 0, 678c2ecf20Sopenharmony_ci .scan_type = { 688c2ecf20Sopenharmony_ci .sign = 's', 698c2ecf20Sopenharmony_ci .realbits = 14, 708c2ecf20Sopenharmony_ci .storagebits = 16, 718c2ecf20Sopenharmony_ci .shift = 2, 728c2ecf20Sopenharmony_ci .endianness = IIO_BE, 738c2ecf20Sopenharmony_ci }, 748c2ecf20Sopenharmony_ci }, 758c2ecf20Sopenharmony_ci { /* cold junction temperature */ 768c2ecf20Sopenharmony_ci .type = IIO_TEMP, 778c2ecf20Sopenharmony_ci .address = 0, 788c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_TEMP_AMBIENT, 798c2ecf20Sopenharmony_ci .modified = 1, 808c2ecf20Sopenharmony_ci .info_mask_separate = 818c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), 828c2ecf20Sopenharmony_ci .scan_index = 1, 838c2ecf20Sopenharmony_ci .scan_type = { 848c2ecf20Sopenharmony_ci .sign = 's', 858c2ecf20Sopenharmony_ci .realbits = 12, 868c2ecf20Sopenharmony_ci .storagebits = 16, 878c2ecf20Sopenharmony_ci .shift = 4, 888c2ecf20Sopenharmony_ci .endianness = IIO_BE, 898c2ecf20Sopenharmony_ci }, 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(2), 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const unsigned long max31855_scan_masks[] = {0x3, 0}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct maxim_thermocouple_chip { 978c2ecf20Sopenharmony_ci const struct iio_chan_spec *channels; 988c2ecf20Sopenharmony_ci const unsigned long *scan_masks; 998c2ecf20Sopenharmony_ci u8 num_channels; 1008c2ecf20Sopenharmony_ci u8 read_size; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* bit-check for valid input */ 1038c2ecf20Sopenharmony_ci u32 status_bit; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = { 1078c2ecf20Sopenharmony_ci [MAX6675] = { 1088c2ecf20Sopenharmony_ci .channels = max6675_channels, 1098c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(max6675_channels), 1108c2ecf20Sopenharmony_ci .read_size = 2, 1118c2ecf20Sopenharmony_ci .status_bit = BIT(2), 1128c2ecf20Sopenharmony_ci }, 1138c2ecf20Sopenharmony_ci [MAX31855] = { 1148c2ecf20Sopenharmony_ci .channels = max31855_channels, 1158c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(max31855_channels), 1168c2ecf20Sopenharmony_ci .read_size = 4, 1178c2ecf20Sopenharmony_ci .scan_masks = max31855_scan_masks, 1188c2ecf20Sopenharmony_ci .status_bit = BIT(16), 1198c2ecf20Sopenharmony_ci }, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct maxim_thermocouple_data { 1238c2ecf20Sopenharmony_ci struct spi_device *spi; 1248c2ecf20Sopenharmony_ci const struct maxim_thermocouple_chip *chip; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci u8 buffer[16] ____cacheline_aligned; 1278c2ecf20Sopenharmony_ci char tc_type; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int maxim_thermocouple_read(struct maxim_thermocouple_data *data, 1318c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, int *val) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned int storage_bytes = data->chip->read_size; 1348c2ecf20Sopenharmony_ci unsigned int shift = chan->scan_type.shift + (chan->address * 8); 1358c2ecf20Sopenharmony_ci __be16 buf16; 1368c2ecf20Sopenharmony_ci __be32 buf32; 1378c2ecf20Sopenharmony_ci int ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci switch (storage_bytes) { 1408c2ecf20Sopenharmony_ci case 2: 1418c2ecf20Sopenharmony_ci ret = spi_read(data->spi, (void *)&buf16, storage_bytes); 1428c2ecf20Sopenharmony_ci *val = be16_to_cpu(buf16); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case 4: 1458c2ecf20Sopenharmony_ci ret = spi_read(data->spi, (void *)&buf32, storage_bytes); 1468c2ecf20Sopenharmony_ci *val = be32_to_cpu(buf32); 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci default: 1498c2ecf20Sopenharmony_ci ret = -EINVAL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* check to be sure this is a valid reading */ 1568c2ecf20Sopenharmony_ci if (*val & data->chip->status_bit) 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci *val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct iio_poll_func *pf = private; 1678c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 1688c2ecf20Sopenharmony_ci struct maxim_thermocouple_data *data = iio_priv(indio_dev); 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = spi_read(data->spi, data->buffer, data->chip->read_size); 1728c2ecf20Sopenharmony_ci if (!ret) { 1738c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, 1748c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int maxim_thermocouple_read_raw(struct iio_dev *indio_dev, 1838c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1848c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct maxim_thermocouple_data *data = iio_priv(indio_dev); 1878c2ecf20Sopenharmony_ci int ret = -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci switch (mask) { 1908c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1918c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 1928c2ecf20Sopenharmony_ci if (ret) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ret = maxim_thermocouple_read(data, chan, val); 1968c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!ret) 1998c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 2038c2ecf20Sopenharmony_ci switch (chan->channel2) { 2048c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: 2058c2ecf20Sopenharmony_ci *val = 62; 2068c2ecf20Sopenharmony_ci *val2 = 500000; /* 1000 * 0.0625 */ 2078c2ecf20Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci default: 2108c2ecf20Sopenharmony_ci *val = 250; /* 1000 * 0.25 */ 2118c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: 2158c2ecf20Sopenharmony_ci *val = data->tc_type; 2168c2ecf20Sopenharmony_ci ret = IIO_VAL_CHAR; 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic const struct iio_info maxim_thermocouple_info = { 2248c2ecf20Sopenharmony_ci .read_raw = maxim_thermocouple_read_raw, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int maxim_thermocouple_probe(struct spi_device *spi) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci const struct spi_device_id *id = spi_get_device_id(spi); 2308c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 2318c2ecf20Sopenharmony_ci struct maxim_thermocouple_data *data; 2328c2ecf20Sopenharmony_ci const int chip_type = (id->driver_data == MAX6675) ? MAX6675 : MAX31855; 2338c2ecf20Sopenharmony_ci const struct maxim_thermocouple_chip *chip = 2348c2ecf20Sopenharmony_ci &maxim_thermocouple_chips[chip_type]; 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); 2388c2ecf20Sopenharmony_ci if (!indio_dev) 2398c2ecf20Sopenharmony_ci return -ENOMEM; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci indio_dev->info = &maxim_thermocouple_info; 2428c2ecf20Sopenharmony_ci indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME; 2438c2ecf20Sopenharmony_ci indio_dev->channels = chip->channels; 2448c2ecf20Sopenharmony_ci indio_dev->available_scan_masks = chip->scan_masks; 2458c2ecf20Sopenharmony_ci indio_dev->num_channels = chip->num_channels; 2468c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 2498c2ecf20Sopenharmony_ci data->spi = spi; 2508c2ecf20Sopenharmony_ci data->chip = chip; 2518c2ecf20Sopenharmony_ci data->tc_type = maxim_tc_types[id->driver_data]; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(&spi->dev, 2548c2ecf20Sopenharmony_ci indio_dev, NULL, 2558c2ecf20Sopenharmony_ci maxim_thermocouple_trigger_handler, NULL); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (id->driver_data == MAX31855) 2608c2ecf20Sopenharmony_ci dev_warn(&spi->dev, "generic max31855 ID is deprecated\nplease use more specific part type"); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return devm_iio_device_register(&spi->dev, indio_dev); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct spi_device_id maxim_thermocouple_id[] = { 2668c2ecf20Sopenharmony_ci {"max6675", MAX6675}, 2678c2ecf20Sopenharmony_ci {"max31855", MAX31855}, 2688c2ecf20Sopenharmony_ci {"max31855k", MAX31855K}, 2698c2ecf20Sopenharmony_ci {"max31855j", MAX31855J}, 2708c2ecf20Sopenharmony_ci {"max31855n", MAX31855N}, 2718c2ecf20Sopenharmony_ci {"max31855s", MAX31855S}, 2728c2ecf20Sopenharmony_ci {"max31855t", MAX31855T}, 2738c2ecf20Sopenharmony_ci {"max31855e", MAX31855E}, 2748c2ecf20Sopenharmony_ci {"max31855r", MAX31855R}, 2758c2ecf20Sopenharmony_ci {}, 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct of_device_id maxim_thermocouple_of_match[] = { 2808c2ecf20Sopenharmony_ci { .compatible = "maxim,max6675" }, 2818c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855" }, 2828c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855k" }, 2838c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855j" }, 2848c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855n" }, 2858c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855s" }, 2868c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855t" }, 2878c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855e" }, 2888c2ecf20Sopenharmony_ci { .compatible = "maxim,max31855r" }, 2898c2ecf20Sopenharmony_ci { }, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, maxim_thermocouple_of_match); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct spi_driver maxim_thermocouple_driver = { 2948c2ecf20Sopenharmony_ci .driver = { 2958c2ecf20Sopenharmony_ci .name = MAXIM_THERMOCOUPLE_DRV_NAME, 2968c2ecf20Sopenharmony_ci .of_match_table = maxim_thermocouple_of_match, 2978c2ecf20Sopenharmony_ci }, 2988c2ecf20Sopenharmony_ci .probe = maxim_thermocouple_probe, 2998c2ecf20Sopenharmony_ci .id_table = maxim_thermocouple_id, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_cimodule_spi_driver(maxim_thermocouple_driver); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim thermocouple sensors"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 306