18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tmp007.c - Support for TI TMP007 IR thermopile sensor with integrated math engine 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Manivannan Sadhasivam <manivannanece23@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Note: 128c2ecf20Sopenharmony_ci * 1. This driver assumes that the sensor has been calibrated beforehand 138c2ecf20Sopenharmony_ci * 2. Limit threshold events are enabled at the start 148c2ecf20Sopenharmony_ci * 3. Operating mode: INT 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/pm.h> 228c2ecf20Sopenharmony_ci#include <linux/bitops.h> 238c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 248c2ecf20Sopenharmony_ci#include <linux/irq.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 288c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 298c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define TMP007_TDIE 0x01 328c2ecf20Sopenharmony_ci#define TMP007_CONFIG 0x02 338c2ecf20Sopenharmony_ci#define TMP007_TOBJECT 0x03 348c2ecf20Sopenharmony_ci#define TMP007_STATUS 0x04 358c2ecf20Sopenharmony_ci#define TMP007_STATUS_MASK 0x05 368c2ecf20Sopenharmony_ci#define TMP007_TOBJ_HIGH_LIMIT 0x06 378c2ecf20Sopenharmony_ci#define TMP007_TOBJ_LOW_LIMIT 0x07 388c2ecf20Sopenharmony_ci#define TMP007_TDIE_HIGH_LIMIT 0x08 398c2ecf20Sopenharmony_ci#define TMP007_TDIE_LOW_LIMIT 0x09 408c2ecf20Sopenharmony_ci#define TMP007_MANUFACTURER_ID 0x1e 418c2ecf20Sopenharmony_ci#define TMP007_DEVICE_ID 0x1f 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define TMP007_CONFIG_CONV_EN BIT(12) 448c2ecf20Sopenharmony_ci#define TMP007_CONFIG_TC_EN BIT(6) 458c2ecf20Sopenharmony_ci#define TMP007_CONFIG_CR_MASK GENMASK(11, 9) 468c2ecf20Sopenharmony_ci#define TMP007_CONFIG_ALERT_EN BIT(8) 478c2ecf20Sopenharmony_ci#define TMP007_CONFIG_CR_SHIFT 9 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Status register flags */ 508c2ecf20Sopenharmony_ci#define TMP007_STATUS_ALERT BIT(15) 518c2ecf20Sopenharmony_ci#define TMP007_STATUS_CONV_READY BIT(14) 528c2ecf20Sopenharmony_ci#define TMP007_STATUS_OHF BIT(13) 538c2ecf20Sopenharmony_ci#define TMP007_STATUS_OLF BIT(12) 548c2ecf20Sopenharmony_ci#define TMP007_STATUS_LHF BIT(11) 558c2ecf20Sopenharmony_ci#define TMP007_STATUS_LLF BIT(10) 568c2ecf20Sopenharmony_ci#define TMP007_STATUS_DATA_VALID BIT(9) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define TMP007_MANUFACTURER_MAGIC 0x5449 598c2ecf20Sopenharmony_ci#define TMP007_DEVICE_MAGIC 0x0078 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define TMP007_TEMP_SHIFT 2 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct tmp007_data { 648c2ecf20Sopenharmony_ci struct i2c_client *client; 658c2ecf20Sopenharmony_ci struct mutex lock; 668c2ecf20Sopenharmony_ci u16 config; 678c2ecf20Sopenharmony_ci u16 status_mask; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const int tmp007_avgs[5][2] = { {4, 0}, {2, 0}, {1, 0}, 718c2ecf20Sopenharmony_ci {0, 500000}, {0, 250000} }; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int tmp007_read_temperature(struct tmp007_data *data, u8 reg) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci s32 ret; 768c2ecf20Sopenharmony_ci int tries = 50; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci while (tries-- > 0) { 798c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, 808c2ecf20Sopenharmony_ci TMP007_STATUS); 818c2ecf20Sopenharmony_ci if (ret < 0) 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci if ((ret & TMP007_STATUS_CONV_READY) && 848c2ecf20Sopenharmony_ci !(ret & TMP007_STATUS_DATA_VALID)) 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci msleep(100); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (tries < 0) 908c2ecf20Sopenharmony_ci return -EIO; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return i2c_smbus_read_word_swapped(data->client, reg); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int tmp007_powerdown(struct tmp007_data *data) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, 988c2ecf20Sopenharmony_ci data->config & ~TMP007_CONFIG_CONV_EN); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int tmp007_read_raw(struct iio_dev *indio_dev, 1028c2ecf20Sopenharmony_ci struct iio_chan_spec const *channel, int *val, 1038c2ecf20Sopenharmony_ci int *val2, long mask) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 1068c2ecf20Sopenharmony_ci s32 ret; 1078c2ecf20Sopenharmony_ci int conv_rate; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci switch (mask) { 1108c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1118c2ecf20Sopenharmony_ci switch (channel->channel2) { 1128c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.03125 degree Celsius */ 1138c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, TMP007_TDIE); 1148c2ecf20Sopenharmony_ci if (ret < 0) 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_OBJECT: 1188c2ecf20Sopenharmony_ci ret = tmp007_read_temperature(data, TMP007_TOBJECT); 1198c2ecf20Sopenharmony_ci if (ret < 0) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci default: 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci *val = sign_extend32(ret, 15) >> TMP007_TEMP_SHIFT; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1298c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1308c2ecf20Sopenharmony_ci *val = 31; 1318c2ecf20Sopenharmony_ci *val2 = 250000; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1348c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 1358c2ecf20Sopenharmony_ci conv_rate = (data->config & TMP007_CONFIG_CR_MASK) 1368c2ecf20Sopenharmony_ci >> TMP007_CONFIG_CR_SHIFT; 1378c2ecf20Sopenharmony_ci *val = tmp007_avgs[conv_rate][0]; 1388c2ecf20Sopenharmony_ci *val2 = tmp007_avgs[conv_rate][1]; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1418c2ecf20Sopenharmony_ci default: 1428c2ecf20Sopenharmony_ci return -EINVAL; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int tmp007_write_raw(struct iio_dev *indio_dev, 1478c2ecf20Sopenharmony_ci struct iio_chan_spec const *channel, int val, 1488c2ecf20Sopenharmony_ci int val2, long mask) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 1518c2ecf20Sopenharmony_ci int i; 1528c2ecf20Sopenharmony_ci u16 tmp; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (mask == IIO_CHAN_INFO_SAMP_FREQ) { 1558c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tmp007_avgs); i++) { 1568c2ecf20Sopenharmony_ci if ((val == tmp007_avgs[i][0]) && 1578c2ecf20Sopenharmony_ci (val2 == tmp007_avgs[i][1])) { 1588c2ecf20Sopenharmony_ci tmp = data->config & ~TMP007_CONFIG_CR_MASK; 1598c2ecf20Sopenharmony_ci tmp |= (i << TMP007_CONFIG_CR_SHIFT); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, 1628c2ecf20Sopenharmony_ci TMP007_CONFIG, 1638c2ecf20Sopenharmony_ci data->config = tmp); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic irqreturn_t tmp007_interrupt_handler(int irq, void *private) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = private; 1748c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS); 1788c2ecf20Sopenharmony_ci if ((ret < 0) || !(ret & (TMP007_STATUS_OHF | TMP007_STATUS_OLF | 1798c2ecf20Sopenharmony_ci TMP007_STATUS_LHF | TMP007_STATUS_LLF))) 1808c2ecf20Sopenharmony_ci return IRQ_NONE; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (ret & TMP007_STATUS_OHF) 1838c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1848c2ecf20Sopenharmony_ci IIO_MOD_EVENT_CODE(IIO_TEMP, 0, 1858c2ecf20Sopenharmony_ci IIO_MOD_TEMP_OBJECT, 1868c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 1878c2ecf20Sopenharmony_ci IIO_EV_DIR_RISING), 1888c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (ret & TMP007_STATUS_OLF) 1918c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1928c2ecf20Sopenharmony_ci IIO_MOD_EVENT_CODE(IIO_TEMP, 0, 1938c2ecf20Sopenharmony_ci IIO_MOD_TEMP_OBJECT, 1948c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 1958c2ecf20Sopenharmony_ci IIO_EV_DIR_FALLING), 1968c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (ret & TMP007_STATUS_LHF) 1998c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 2008c2ecf20Sopenharmony_ci IIO_MOD_EVENT_CODE(IIO_TEMP, 0, 2018c2ecf20Sopenharmony_ci IIO_MOD_TEMP_AMBIENT, 2028c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 2038c2ecf20Sopenharmony_ci IIO_EV_DIR_RISING), 2048c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (ret & TMP007_STATUS_LLF) 2078c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 2088c2ecf20Sopenharmony_ci IIO_MOD_EVENT_CODE(IIO_TEMP, 0, 2098c2ecf20Sopenharmony_ci IIO_MOD_TEMP_AMBIENT, 2108c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 2118c2ecf20Sopenharmony_ci IIO_EV_DIR_FALLING), 2128c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int tmp007_write_event_config(struct iio_dev *indio_dev, 2188c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 2198c2ecf20Sopenharmony_ci enum iio_event_direction dir, int state) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 2228c2ecf20Sopenharmony_ci unsigned int status_mask; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci switch (chan->channel2) { 2268c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: 2278c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 2288c2ecf20Sopenharmony_ci status_mask = TMP007_STATUS_LHF; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci status_mask = TMP007_STATUS_LLF; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_OBJECT: 2338c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 2348c2ecf20Sopenharmony_ci status_mask = TMP007_STATUS_OHF; 2358c2ecf20Sopenharmony_ci else 2368c2ecf20Sopenharmony_ci status_mask = TMP007_STATUS_OLF; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci default: 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 2438c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK); 2448c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2458c2ecf20Sopenharmony_ci if (ret < 0) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (state) 2498c2ecf20Sopenharmony_ci ret |= status_mask; 2508c2ecf20Sopenharmony_ci else 2518c2ecf20Sopenharmony_ci ret &= ~status_mask; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, 2548c2ecf20Sopenharmony_ci data->status_mask = ret); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int tmp007_read_event_config(struct iio_dev *indio_dev, 2588c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 2598c2ecf20Sopenharmony_ci enum iio_event_direction dir) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 2628c2ecf20Sopenharmony_ci unsigned int mask; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci switch (chan->channel2) { 2658c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: 2668c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 2678c2ecf20Sopenharmony_ci mask = TMP007_STATUS_LHF; 2688c2ecf20Sopenharmony_ci else 2698c2ecf20Sopenharmony_ci mask = TMP007_STATUS_LLF; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_OBJECT: 2728c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 2738c2ecf20Sopenharmony_ci mask = TMP007_STATUS_OHF; 2748c2ecf20Sopenharmony_ci else 2758c2ecf20Sopenharmony_ci mask = TMP007_STATUS_OLF; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return !!(data->status_mask & mask); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int tmp007_read_thresh(struct iio_dev *indio_dev, 2858c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 2868c2ecf20Sopenharmony_ci enum iio_event_direction dir, enum iio_event_info info, 2878c2ecf20Sopenharmony_ci int *val, int *val2) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 2908c2ecf20Sopenharmony_ci int ret; 2918c2ecf20Sopenharmony_ci u8 reg; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci switch (chan->channel2) { 2948c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.5 degree Celsius */ 2958c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 2968c2ecf20Sopenharmony_ci reg = TMP007_TDIE_HIGH_LIMIT; 2978c2ecf20Sopenharmony_ci else 2988c2ecf20Sopenharmony_ci reg = TMP007_TDIE_LOW_LIMIT; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_OBJECT: 3018c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 3028c2ecf20Sopenharmony_ci reg = TMP007_TOBJ_HIGH_LIMIT; 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci reg = TMP007_TOBJ_LOW_LIMIT; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci default: 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, reg); 3118c2ecf20Sopenharmony_ci if (ret < 0) 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Shift length 7 bits = 6(15:6) + 1(0.5 LSB) */ 3158c2ecf20Sopenharmony_ci *val = sign_extend32(ret, 15) >> 7; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int tmp007_write_thresh(struct iio_dev *indio_dev, 3218c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 3228c2ecf20Sopenharmony_ci enum iio_event_direction dir, enum iio_event_info info, 3238c2ecf20Sopenharmony_ci int val, int val2) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 3268c2ecf20Sopenharmony_ci u8 reg; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci switch (chan->channel2) { 3298c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_AMBIENT: 3308c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 3318c2ecf20Sopenharmony_ci reg = TMP007_TDIE_HIGH_LIMIT; 3328c2ecf20Sopenharmony_ci else 3338c2ecf20Sopenharmony_ci reg = TMP007_TDIE_LOW_LIMIT; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case IIO_MOD_TEMP_OBJECT: 3368c2ecf20Sopenharmony_ci if (dir == IIO_EV_DIR_RISING) 3378c2ecf20Sopenharmony_ci reg = TMP007_TOBJ_HIGH_LIMIT; 3388c2ecf20Sopenharmony_ci else 3398c2ecf20Sopenharmony_ci reg = TMP007_TOBJ_LOW_LIMIT; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci default: 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Full scale threshold value is +/- 256 degree Celsius */ 3468c2ecf20Sopenharmony_ci if (val < -256 || val > 255) 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Shift length 7 bits = 6(15:6) + 1(0.5 LSB) */ 3508c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, reg, (val << 7)); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25"); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic struct attribute *tmp007_attributes[] = { 3568c2ecf20Sopenharmony_ci &iio_const_attr_sampling_frequency_available.dev_attr.attr, 3578c2ecf20Sopenharmony_ci NULL 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic const struct attribute_group tmp007_attribute_group = { 3618c2ecf20Sopenharmony_ci .attrs = tmp007_attributes, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const struct iio_event_spec tmp007_obj_event[] = { 3658c2ecf20Sopenharmony_ci { 3668c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 3678c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 3688c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 3698c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 3708c2ecf20Sopenharmony_ci }, 3718c2ecf20Sopenharmony_ci { 3728c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 3738c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 3748c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 3758c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 3768c2ecf20Sopenharmony_ci }, 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic const struct iio_event_spec tmp007_die_event[] = { 3808c2ecf20Sopenharmony_ci { 3818c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 3828c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 3838c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 3848c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 3858c2ecf20Sopenharmony_ci }, 3868c2ecf20Sopenharmony_ci { 3878c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 3888c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 3898c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 3908c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci}; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic const struct iio_chan_spec tmp007_channels[] = { 3958c2ecf20Sopenharmony_ci { 3968c2ecf20Sopenharmony_ci .type = IIO_TEMP, 3978c2ecf20Sopenharmony_ci .modified = 1, 3988c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_TEMP_AMBIENT, 3998c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 4008c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 4018c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 4028c2ecf20Sopenharmony_ci .event_spec = tmp007_die_event, 4038c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tmp007_die_event), 4048c2ecf20Sopenharmony_ci }, 4058c2ecf20Sopenharmony_ci { 4068c2ecf20Sopenharmony_ci .type = IIO_TEMP, 4078c2ecf20Sopenharmony_ci .modified = 1, 4088c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_TEMP_OBJECT, 4098c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 4108c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 4118c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 4128c2ecf20Sopenharmony_ci .event_spec = tmp007_obj_event, 4138c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tmp007_obj_event), 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct iio_info tmp007_info = { 4188c2ecf20Sopenharmony_ci .read_raw = tmp007_read_raw, 4198c2ecf20Sopenharmony_ci .write_raw = tmp007_write_raw, 4208c2ecf20Sopenharmony_ci .read_event_config = tmp007_read_event_config, 4218c2ecf20Sopenharmony_ci .write_event_config = tmp007_write_event_config, 4228c2ecf20Sopenharmony_ci .read_event_value = tmp007_read_thresh, 4238c2ecf20Sopenharmony_ci .write_event_value = tmp007_write_thresh, 4248c2ecf20Sopenharmony_ci .attrs = &tmp007_attribute_group, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic bool tmp007_identify(struct i2c_client *client) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci int manf_id, dev_id; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci manf_id = i2c_smbus_read_word_swapped(client, TMP007_MANUFACTURER_ID); 4328c2ecf20Sopenharmony_ci if (manf_id < 0) 4338c2ecf20Sopenharmony_ci return false; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci dev_id = i2c_smbus_read_word_swapped(client, TMP007_DEVICE_ID); 4368c2ecf20Sopenharmony_ci if (dev_id < 0) 4378c2ecf20Sopenharmony_ci return false; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int tmp007_probe(struct i2c_client *client, 4438c2ecf20Sopenharmony_ci const struct i2c_device_id *tmp007_id) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct tmp007_data *data; 4468c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 4478c2ecf20Sopenharmony_ci int ret; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) 4508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!tmp007_identify(client)) { 4538c2ecf20Sopenharmony_ci dev_err(&client->dev, "TMP007 not found\n"); 4548c2ecf20Sopenharmony_ci return -ENODEV; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 4588c2ecf20Sopenharmony_ci if (!indio_dev) 4598c2ecf20Sopenharmony_ci return -ENOMEM; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 4628c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 4638c2ecf20Sopenharmony_ci data->client = client; 4648c2ecf20Sopenharmony_ci mutex_init(&data->lock); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci indio_dev->name = "tmp007"; 4678c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 4688c2ecf20Sopenharmony_ci indio_dev->info = &tmp007_info; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci indio_dev->channels = tmp007_channels; 4718c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(tmp007_channels); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * Set Configuration register: 4758c2ecf20Sopenharmony_ci * 1. Conversion ON 4768c2ecf20Sopenharmony_ci * 2. ALERT enable 4778c2ecf20Sopenharmony_ci * 3. Transient correction enable 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, TMP007_CONFIG); 4818c2ecf20Sopenharmony_ci if (ret < 0) 4828c2ecf20Sopenharmony_ci return ret; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci data->config = ret; 4858c2ecf20Sopenharmony_ci data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_ALERT_EN | TMP007_CONFIG_TC_EN); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, 4888c2ecf20Sopenharmony_ci data->config); 4898c2ecf20Sopenharmony_ci if (ret < 0) 4908c2ecf20Sopenharmony_ci return ret; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* 4938c2ecf20Sopenharmony_ci * Only the following flags can activate ALERT pin. Data conversion/validity flags 4948c2ecf20Sopenharmony_ci * flags can still be polled for getting temperature data 4958c2ecf20Sopenharmony_ci * 4968c2ecf20Sopenharmony_ci * Set Status Mask register: 4978c2ecf20Sopenharmony_ci * 1. Object temperature high limit enable 4988c2ecf20Sopenharmony_ci * 2. Object temperature low limit enable 4998c2ecf20Sopenharmony_ci * 3. TDIE temperature high limit enable 5008c2ecf20Sopenharmony_ci * 4. TDIE temperature low limit enable 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK); 5048c2ecf20Sopenharmony_ci if (ret < 0) 5058c2ecf20Sopenharmony_ci goto error_powerdown; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci data->status_mask = ret; 5088c2ecf20Sopenharmony_ci data->status_mask |= (TMP007_STATUS_OHF | TMP007_STATUS_OLF 5098c2ecf20Sopenharmony_ci | TMP007_STATUS_LHF | TMP007_STATUS_LLF); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, data->status_mask); 5128c2ecf20Sopenharmony_ci if (ret < 0) 5138c2ecf20Sopenharmony_ci goto error_powerdown; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (client->irq) { 5168c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 5178c2ecf20Sopenharmony_ci NULL, tmp007_interrupt_handler, 5188c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 5198c2ecf20Sopenharmony_ci tmp007_id->name, indio_dev); 5208c2ecf20Sopenharmony_ci if (ret) { 5218c2ecf20Sopenharmony_ci dev_err(&client->dev, "irq request error %d\n", -ret); 5228c2ecf20Sopenharmony_ci goto error_powerdown; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return iio_device_register(indio_dev); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cierror_powerdown: 5298c2ecf20Sopenharmony_ci tmp007_powerdown(data); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return ret; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int tmp007_remove(struct i2c_client *client) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 5378c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(indio_dev); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 5408c2ecf20Sopenharmony_ci tmp007_powerdown(data); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5468c2ecf20Sopenharmony_cistatic int tmp007_suspend(struct device *dev) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(i2c_get_clientdata( 5498c2ecf20Sopenharmony_ci to_i2c_client(dev))); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return tmp007_powerdown(data); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int tmp007_resume(struct device *dev) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct tmp007_data *data = iio_priv(i2c_get_clientdata( 5578c2ecf20Sopenharmony_ci to_i2c_client(dev))); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, 5608c2ecf20Sopenharmony_ci data->config | TMP007_CONFIG_CONV_EN); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci#endif 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic const struct of_device_id tmp007_of_match[] = { 5678c2ecf20Sopenharmony_ci { .compatible = "ti,tmp007", }, 5688c2ecf20Sopenharmony_ci { }, 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tmp007_of_match); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic const struct i2c_device_id tmp007_id[] = { 5738c2ecf20Sopenharmony_ci { "tmp007", 0 }, 5748c2ecf20Sopenharmony_ci { } 5758c2ecf20Sopenharmony_ci}; 5768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tmp007_id); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic struct i2c_driver tmp007_driver = { 5798c2ecf20Sopenharmony_ci .driver = { 5808c2ecf20Sopenharmony_ci .name = "tmp007", 5818c2ecf20Sopenharmony_ci .of_match_table = tmp007_of_match, 5828c2ecf20Sopenharmony_ci .pm = &tmp007_pm_ops, 5838c2ecf20Sopenharmony_ci }, 5848c2ecf20Sopenharmony_ci .probe = tmp007_probe, 5858c2ecf20Sopenharmony_ci .remove = tmp007_remove, 5868c2ecf20Sopenharmony_ci .id_table = tmp007_id, 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_cimodule_i2c_driver(tmp007_driver); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manivannan Sadhasivam <manivannanece23@gmail.com>"); 5918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI TMP007 IR thermopile sensor driver"); 5928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 593