18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tmp006.c - Support for TI TMP006 IR thermopile sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net> 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, changeable via ADR pins) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * TODO: data ready irq 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/pm.h> 198c2ecf20Sopenharmony_ci#include <linux/bitops.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define TMP006_VOBJECT 0x00 258c2ecf20Sopenharmony_ci#define TMP006_TAMBIENT 0x01 268c2ecf20Sopenharmony_ci#define TMP006_CONFIG 0x02 278c2ecf20Sopenharmony_ci#define TMP006_MANUFACTURER_ID 0xfe 288c2ecf20Sopenharmony_ci#define TMP006_DEVICE_ID 0xff 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define TMP006_TAMBIENT_SHIFT 2 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define TMP006_CONFIG_RESET BIT(15) 338c2ecf20Sopenharmony_ci#define TMP006_CONFIG_DRDY_EN BIT(8) 348c2ecf20Sopenharmony_ci#define TMP006_CONFIG_DRDY BIT(7) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define TMP006_CONFIG_MOD_MASK GENMASK(14, 12) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TMP006_CONFIG_CR_MASK GENMASK(11, 9) 398c2ecf20Sopenharmony_ci#define TMP006_CONFIG_CR_SHIFT 9 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define TMP006_MANUFACTURER_MAGIC 0x5449 428c2ecf20Sopenharmony_ci#define TMP006_DEVICE_MAGIC 0x0067 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct tmp006_data { 458c2ecf20Sopenharmony_ci struct i2c_client *client; 468c2ecf20Sopenharmony_ci u16 config; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int tmp006_read_measurement(struct tmp006_data *data, u8 reg) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci s32 ret; 528c2ecf20Sopenharmony_ci int tries = 50; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci while (tries-- > 0) { 558c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, 568c2ecf20Sopenharmony_ci TMP006_CONFIG); 578c2ecf20Sopenharmony_ci if (ret < 0) 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci if (ret & TMP006_CONFIG_DRDY) 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci msleep(100); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (tries < 0) 658c2ecf20Sopenharmony_ci return -EIO; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return i2c_smbus_read_word_swapped(data->client, reg); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const int tmp006_freqs[5][2] = { {4, 0}, {2, 0}, {1, 0}, 718c2ecf20Sopenharmony_ci {0, 500000}, {0, 250000} }; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int tmp006_read_raw(struct iio_dev *indio_dev, 748c2ecf20Sopenharmony_ci struct iio_chan_spec const *channel, int *val, 758c2ecf20Sopenharmony_ci int *val2, long mask) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct tmp006_data *data = iio_priv(indio_dev); 788c2ecf20Sopenharmony_ci s32 ret; 798c2ecf20Sopenharmony_ci int cr; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci switch (mask) { 828c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 838c2ecf20Sopenharmony_ci if (channel->type == IIO_VOLTAGE) { 848c2ecf20Sopenharmony_ci /* LSB is 156.25 nV */ 858c2ecf20Sopenharmony_ci ret = tmp006_read_measurement(data, TMP006_VOBJECT); 868c2ecf20Sopenharmony_ci if (ret < 0) 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci *val = sign_extend32(ret, 15); 898c2ecf20Sopenharmony_ci } else if (channel->type == IIO_TEMP) { 908c2ecf20Sopenharmony_ci /* LSB is 0.03125 degrees Celsius */ 918c2ecf20Sopenharmony_ci ret = tmp006_read_measurement(data, TMP006_TAMBIENT); 928c2ecf20Sopenharmony_ci if (ret < 0) 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci *val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT; 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return IIO_VAL_INT; 998c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1008c2ecf20Sopenharmony_ci if (channel->type == IIO_VOLTAGE) { 1018c2ecf20Sopenharmony_ci *val = 0; 1028c2ecf20Sopenharmony_ci *val2 = 156250; 1038c2ecf20Sopenharmony_ci } else if (channel->type == IIO_TEMP) { 1048c2ecf20Sopenharmony_ci *val = 31; 1058c2ecf20Sopenharmony_ci *val2 = 250000; 1068c2ecf20Sopenharmony_ci } else { 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1108c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 1118c2ecf20Sopenharmony_ci cr = (data->config & TMP006_CONFIG_CR_MASK) 1128c2ecf20Sopenharmony_ci >> TMP006_CONFIG_CR_SHIFT; 1138c2ecf20Sopenharmony_ci *val = tmp006_freqs[cr][0]; 1148c2ecf20Sopenharmony_ci *val2 = tmp006_freqs[cr][1]; 1158c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1168c2ecf20Sopenharmony_ci default: 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int tmp006_write_raw(struct iio_dev *indio_dev, 1248c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1258c2ecf20Sopenharmony_ci int val, 1268c2ecf20Sopenharmony_ci int val2, 1278c2ecf20Sopenharmony_ci long mask) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct tmp006_data *data = iio_priv(indio_dev); 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (mask != IIO_CHAN_INFO_SAMP_FREQ) 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++) 1368c2ecf20Sopenharmony_ci if ((val == tmp006_freqs[i][0]) && 1378c2ecf20Sopenharmony_ci (val2 == tmp006_freqs[i][1])) { 1388c2ecf20Sopenharmony_ci data->config &= ~TMP006_CONFIG_CR_MASK; 1398c2ecf20Sopenharmony_ci data->config |= i << TMP006_CONFIG_CR_SHIFT; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, 1428c2ecf20Sopenharmony_ci TMP006_CONFIG, 1438c2ecf20Sopenharmony_ci data->config); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci return -EINVAL; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25"); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic struct attribute *tmp006_attributes[] = { 1528c2ecf20Sopenharmony_ci &iio_const_attr_sampling_frequency_available.dev_attr.attr, 1538c2ecf20Sopenharmony_ci NULL 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct attribute_group tmp006_attribute_group = { 1578c2ecf20Sopenharmony_ci .attrs = tmp006_attributes, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct iio_chan_spec tmp006_channels[] = { 1618c2ecf20Sopenharmony_ci { 1628c2ecf20Sopenharmony_ci .type = IIO_VOLTAGE, 1638c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1648c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1658c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1668c2ecf20Sopenharmony_ci }, 1678c2ecf20Sopenharmony_ci { 1688c2ecf20Sopenharmony_ci .type = IIO_TEMP, 1698c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1708c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1718c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic const struct iio_info tmp006_info = { 1768c2ecf20Sopenharmony_ci .read_raw = tmp006_read_raw, 1778c2ecf20Sopenharmony_ci .write_raw = tmp006_write_raw, 1788c2ecf20Sopenharmony_ci .attrs = &tmp006_attribute_group, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic bool tmp006_check_identification(struct i2c_client *client) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int mid, did; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mid = i2c_smbus_read_word_swapped(client, TMP006_MANUFACTURER_ID); 1868c2ecf20Sopenharmony_ci if (mid < 0) 1878c2ecf20Sopenharmony_ci return false; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci did = i2c_smbus_read_word_swapped(client, TMP006_DEVICE_ID); 1908c2ecf20Sopenharmony_ci if (did < 0) 1918c2ecf20Sopenharmony_ci return false; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int tmp006_probe(struct i2c_client *client, 1978c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 2008c2ecf20Sopenharmony_ci struct tmp006_data *data; 2018c2ecf20Sopenharmony_ci int ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) 2048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!tmp006_check_identification(client)) { 2078c2ecf20Sopenharmony_ci dev_err(&client->dev, "no TMP006 sensor\n"); 2088c2ecf20Sopenharmony_ci return -ENODEV; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 2128c2ecf20Sopenharmony_ci if (!indio_dev) 2138c2ecf20Sopenharmony_ci return -ENOMEM; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 2168c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 2178c2ecf20Sopenharmony_ci data->client = client; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci indio_dev->name = dev_name(&client->dev); 2208c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2218c2ecf20Sopenharmony_ci indio_dev->info = &tmp006_info; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci indio_dev->channels = tmp006_channels; 2248c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(tmp006_channels); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, TMP006_CONFIG); 2278c2ecf20Sopenharmony_ci if (ret < 0) 2288c2ecf20Sopenharmony_ci return ret; 2298c2ecf20Sopenharmony_ci data->config = ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return iio_device_register(indio_dev); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int tmp006_powerdown(struct tmp006_data *data) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG, 2378c2ecf20Sopenharmony_ci data->config & ~TMP006_CONFIG_MOD_MASK); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int tmp006_remove(struct i2c_client *client) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 2458c2ecf20Sopenharmony_ci tmp006_powerdown(iio_priv(indio_dev)); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2518c2ecf20Sopenharmony_cistatic int tmp006_suspend(struct device *dev) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 2548c2ecf20Sopenharmony_ci return tmp006_powerdown(iio_priv(indio_dev)); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int tmp006_resume(struct device *dev) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct tmp006_data *data = iio_priv(i2c_get_clientdata( 2608c2ecf20Sopenharmony_ci to_i2c_client(dev))); 2618c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG, 2628c2ecf20Sopenharmony_ci data->config | TMP006_CONFIG_MOD_MASK); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci#endif 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tmp006_pm_ops, tmp006_suspend, tmp006_resume); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct i2c_device_id tmp006_id[] = { 2698c2ecf20Sopenharmony_ci { "tmp006", 0 }, 2708c2ecf20Sopenharmony_ci { } 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tmp006_id); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic struct i2c_driver tmp006_driver = { 2758c2ecf20Sopenharmony_ci .driver = { 2768c2ecf20Sopenharmony_ci .name = "tmp006", 2778c2ecf20Sopenharmony_ci .pm = &tmp006_pm_ops, 2788c2ecf20Sopenharmony_ci }, 2798c2ecf20Sopenharmony_ci .probe = tmp006_probe, 2808c2ecf20Sopenharmony_ci .remove = tmp006_remove, 2818c2ecf20Sopenharmony_ci .id_table = tmp006_id, 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_cimodule_i2c_driver(tmp006_driver); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 2868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI TMP006 IR thermopile sensor driver"); 2878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 288