xref: /openbmc/linux/drivers/iio/temperature/tmp117.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1df041e73SPuranjay Mohan // SPDX-License-Identifier: GPL-2.0-only
2df041e73SPuranjay Mohan /*
3df041e73SPuranjay Mohan  * Digital temperature sensor with integrated Non-volatile memory
4df041e73SPuranjay Mohan  * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com>
5df041e73SPuranjay Mohan  *
6df041e73SPuranjay Mohan  * Driver for the Texas Instruments TMP117 Temperature Sensor
7df041e73SPuranjay Mohan  * (7-bit I2C slave address (0x48 - 0x4B), changeable via ADD pins)
8df041e73SPuranjay Mohan  *
9df041e73SPuranjay Mohan  * Note: This driver assumes that the sensor has been calibrated beforehand.
10df041e73SPuranjay Mohan  */
11df041e73SPuranjay Mohan 
12df041e73SPuranjay Mohan #include <linux/err.h>
13df041e73SPuranjay Mohan #include <linux/i2c.h>
14df041e73SPuranjay Mohan #include <linux/module.h>
15df041e73SPuranjay Mohan #include <linux/bitops.h>
16df041e73SPuranjay Mohan #include <linux/types.h>
17df041e73SPuranjay Mohan #include <linux/kernel.h>
18df041e73SPuranjay Mohan #include <linux/limits.h>
1907cc6899SMarco Felsch #include <linux/property.h>
20df041e73SPuranjay Mohan 
21df041e73SPuranjay Mohan #include <linux/iio/iio.h>
22df041e73SPuranjay Mohan 
23df041e73SPuranjay Mohan #define TMP117_REG_TEMP			0x0
24df041e73SPuranjay Mohan #define TMP117_REG_CFGR			0x1
25df041e73SPuranjay Mohan #define TMP117_REG_HIGH_LIM		0x2
26df041e73SPuranjay Mohan #define TMP117_REG_LOW_LIM		0x3
27df041e73SPuranjay Mohan #define TMP117_REG_EEPROM_UL		0x4
28df041e73SPuranjay Mohan #define TMP117_REG_EEPROM1		0x5
29df041e73SPuranjay Mohan #define TMP117_REG_EEPROM2		0x6
30df041e73SPuranjay Mohan #define TMP117_REG_TEMP_OFFSET		0x7
31df041e73SPuranjay Mohan #define TMP117_REG_EEPROM3		0x8
32df041e73SPuranjay Mohan #define TMP117_REG_DEVICE_ID		0xF
33df041e73SPuranjay Mohan 
34df041e73SPuranjay Mohan #define TMP117_RESOLUTION_10UC		78125
35df041e73SPuranjay Mohan #define MICRODEGREE_PER_10MILLIDEGREE	10000
36df041e73SPuranjay Mohan 
370cd2889dSMarco Felsch #define TMP116_DEVICE_ID		0x1116
380cd2889dSMarco Felsch #define TMP117_DEVICE_ID		0x0117
390cd2889dSMarco Felsch 
40df041e73SPuranjay Mohan struct tmp117_data {
41df041e73SPuranjay Mohan 	struct i2c_client *client;
42df041e73SPuranjay Mohan 	s16 calibbias;
43df041e73SPuranjay Mohan };
44df041e73SPuranjay Mohan 
tmp117_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * channel,int * val,int * val2,long mask)45df041e73SPuranjay Mohan static int tmp117_read_raw(struct iio_dev *indio_dev,
46df041e73SPuranjay Mohan 			   struct iio_chan_spec const *channel, int *val,
47df041e73SPuranjay Mohan 			   int *val2, long mask)
48df041e73SPuranjay Mohan {
49df041e73SPuranjay Mohan 	struct tmp117_data *data = iio_priv(indio_dev);
50df041e73SPuranjay Mohan 	s32 ret;
51df041e73SPuranjay Mohan 
52df041e73SPuranjay Mohan 	switch (mask) {
53df041e73SPuranjay Mohan 	case IIO_CHAN_INFO_RAW:
54df041e73SPuranjay Mohan 		ret = i2c_smbus_read_word_swapped(data->client,
55df041e73SPuranjay Mohan 						  TMP117_REG_TEMP);
56df041e73SPuranjay Mohan 		if (ret < 0)
57df041e73SPuranjay Mohan 			return ret;
58df041e73SPuranjay Mohan 		*val = sign_extend32(ret, 15);
59df041e73SPuranjay Mohan 		return IIO_VAL_INT;
60df041e73SPuranjay Mohan 
61df041e73SPuranjay Mohan 	case IIO_CHAN_INFO_CALIBBIAS:
62df041e73SPuranjay Mohan 		ret = i2c_smbus_read_word_swapped(data->client,
63df041e73SPuranjay Mohan 						  TMP117_REG_TEMP_OFFSET);
64df041e73SPuranjay Mohan 		if (ret < 0)
65df041e73SPuranjay Mohan 			return ret;
66df041e73SPuranjay Mohan 		*val = sign_extend32(ret, 15);
67df041e73SPuranjay Mohan 		return IIO_VAL_INT;
68df041e73SPuranjay Mohan 
69df041e73SPuranjay Mohan 	case IIO_CHAN_INFO_SCALE:
70df041e73SPuranjay Mohan 		/*
71df041e73SPuranjay Mohan 		 * Conversion from 10s of uC to mC
72df041e73SPuranjay Mohan 		 * as IIO reports temperature in mC
73df041e73SPuranjay Mohan 		 */
74df041e73SPuranjay Mohan 		*val = TMP117_RESOLUTION_10UC / MICRODEGREE_PER_10MILLIDEGREE;
75df041e73SPuranjay Mohan 		*val2 = (TMP117_RESOLUTION_10UC %
76df041e73SPuranjay Mohan 					MICRODEGREE_PER_10MILLIDEGREE) * 100;
77df041e73SPuranjay Mohan 
78df041e73SPuranjay Mohan 		return IIO_VAL_INT_PLUS_MICRO;
79df041e73SPuranjay Mohan 
80df041e73SPuranjay Mohan 	default:
81df041e73SPuranjay Mohan 		return -EINVAL;
82df041e73SPuranjay Mohan 	}
83df041e73SPuranjay Mohan }
84df041e73SPuranjay Mohan 
tmp117_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * channel,int val,int val2,long mask)85*e9423075SMarco Felsch static int tmp117_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec
86*e9423075SMarco Felsch 			    const *channel, int val, int val2, long mask)
87df041e73SPuranjay Mohan {
88df041e73SPuranjay Mohan 	struct tmp117_data *data = iio_priv(indio_dev);
89df041e73SPuranjay Mohan 	s16 off;
90df041e73SPuranjay Mohan 
91df041e73SPuranjay Mohan 	switch (mask) {
92df041e73SPuranjay Mohan 	case IIO_CHAN_INFO_CALIBBIAS:
93df041e73SPuranjay Mohan 		off = clamp_t(int, val, S16_MIN, S16_MAX);
94df041e73SPuranjay Mohan 		if (off == data->calibbias)
95df041e73SPuranjay Mohan 			return 0;
96df041e73SPuranjay Mohan 		data->calibbias = off;
97df041e73SPuranjay Mohan 		return i2c_smbus_write_word_swapped(data->client,
98df041e73SPuranjay Mohan 						TMP117_REG_TEMP_OFFSET, off);
99df041e73SPuranjay Mohan 
100df041e73SPuranjay Mohan 	default:
101df041e73SPuranjay Mohan 		return -EINVAL;
102df041e73SPuranjay Mohan 	}
103df041e73SPuranjay Mohan }
104df041e73SPuranjay Mohan 
105df041e73SPuranjay Mohan static const struct iio_chan_spec tmp117_channels[] = {
106df041e73SPuranjay Mohan 	{
107df041e73SPuranjay Mohan 		.type = IIO_TEMP,
108df041e73SPuranjay Mohan 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
109*e9423075SMarco Felsch 				      BIT(IIO_CHAN_INFO_CALIBBIAS) |
110*e9423075SMarco Felsch 				      BIT(IIO_CHAN_INFO_SCALE),
111df041e73SPuranjay Mohan 	},
112df041e73SPuranjay Mohan };
113df041e73SPuranjay Mohan 
1140cd2889dSMarco Felsch static const struct iio_chan_spec tmp116_channels[] = {
1150cd2889dSMarco Felsch 	{
1160cd2889dSMarco Felsch 		.type = IIO_TEMP,
1170cd2889dSMarco Felsch 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1180cd2889dSMarco Felsch 				      BIT(IIO_CHAN_INFO_SCALE),
1190cd2889dSMarco Felsch 	},
1200cd2889dSMarco Felsch };
1210cd2889dSMarco Felsch 
122df041e73SPuranjay Mohan static const struct iio_info tmp117_info = {
123df041e73SPuranjay Mohan 	.read_raw = tmp117_read_raw,
124df041e73SPuranjay Mohan 	.write_raw = tmp117_write_raw,
125df041e73SPuranjay Mohan };
126df041e73SPuranjay Mohan 
tmp117_identify(struct i2c_client * client)127df041e73SPuranjay Mohan static int tmp117_identify(struct i2c_client *client)
128df041e73SPuranjay Mohan {
12907cc6899SMarco Felsch 	const struct i2c_device_id *id;
13007cc6899SMarco Felsch 	unsigned long match_data;
131df041e73SPuranjay Mohan 	int dev_id;
132df041e73SPuranjay Mohan 
133df041e73SPuranjay Mohan 	dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID);
134df041e73SPuranjay Mohan 	if (dev_id < 0)
135df041e73SPuranjay Mohan 		return dev_id;
13607cc6899SMarco Felsch 
13707cc6899SMarco Felsch 	switch (dev_id) {
1380cd2889dSMarco Felsch 	case TMP116_DEVICE_ID:
13907cc6899SMarco Felsch 	case TMP117_DEVICE_ID:
14007cc6899SMarco Felsch 		return dev_id;
141df041e73SPuranjay Mohan 	}
14207cc6899SMarco Felsch 
14307cc6899SMarco Felsch 	dev_info(&client->dev, "Unknown device id (0x%x), use fallback compatible\n",
14407cc6899SMarco Felsch 		 dev_id);
14507cc6899SMarco Felsch 
14607cc6899SMarco Felsch 	match_data = (uintptr_t)device_get_match_data(&client->dev);
14707cc6899SMarco Felsch 	if (match_data)
14807cc6899SMarco Felsch 		return match_data;
14907cc6899SMarco Felsch 
15007cc6899SMarco Felsch 	id = i2c_client_get_device_id(client);
15107cc6899SMarco Felsch 	if (id)
15207cc6899SMarco Felsch 		return id->driver_data;
15307cc6899SMarco Felsch 
15407cc6899SMarco Felsch 	dev_err(&client->dev, "Failed to identify unsupported device\n");
15507cc6899SMarco Felsch 
15607cc6899SMarco Felsch 	return -ENODEV;
157df041e73SPuranjay Mohan }
158df041e73SPuranjay Mohan 
tmp117_probe(struct i2c_client * client)159df041e73SPuranjay Mohan static int tmp117_probe(struct i2c_client *client)
160df041e73SPuranjay Mohan {
161df041e73SPuranjay Mohan 	struct tmp117_data *data;
162df041e73SPuranjay Mohan 	struct iio_dev *indio_dev;
16307cc6899SMarco Felsch 	int ret, dev_id;
164df041e73SPuranjay Mohan 
165df041e73SPuranjay Mohan 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
166df041e73SPuranjay Mohan 		return -EOPNOTSUPP;
167df041e73SPuranjay Mohan 
168df041e73SPuranjay Mohan 	ret = tmp117_identify(client);
169df041e73SPuranjay Mohan 	if (ret < 0)
170df041e73SPuranjay Mohan 		return ret;
171df041e73SPuranjay Mohan 
17207cc6899SMarco Felsch 	dev_id = ret;
17307cc6899SMarco Felsch 
174df041e73SPuranjay Mohan 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
175df041e73SPuranjay Mohan 	if (!indio_dev)
176df041e73SPuranjay Mohan 		return -ENOMEM;
177df041e73SPuranjay Mohan 
178df041e73SPuranjay Mohan 	data = iio_priv(indio_dev);
179df041e73SPuranjay Mohan 	data->client = client;
180df041e73SPuranjay Mohan 	data->calibbias = 0;
181df041e73SPuranjay Mohan 
182df041e73SPuranjay Mohan 	indio_dev->modes = INDIO_DIRECT_MODE;
183df041e73SPuranjay Mohan 	indio_dev->info = &tmp117_info;
184df041e73SPuranjay Mohan 
18507cc6899SMarco Felsch 	switch (dev_id) {
1860cd2889dSMarco Felsch 	case TMP116_DEVICE_ID:
1870cd2889dSMarco Felsch 		indio_dev->channels = tmp116_channels;
1880cd2889dSMarco Felsch 		indio_dev->num_channels = ARRAY_SIZE(tmp116_channels);
1890cd2889dSMarco Felsch 		indio_dev->name = "tmp116";
1900cd2889dSMarco Felsch 		break;
19107cc6899SMarco Felsch 	case TMP117_DEVICE_ID:
192df041e73SPuranjay Mohan 		indio_dev->channels = tmp117_channels;
193df041e73SPuranjay Mohan 		indio_dev->num_channels = ARRAY_SIZE(tmp117_channels);
19407cc6899SMarco Felsch 		indio_dev->name = "tmp117";
19507cc6899SMarco Felsch 		break;
19607cc6899SMarco Felsch 	}
197df041e73SPuranjay Mohan 
198df041e73SPuranjay Mohan 	return devm_iio_device_register(&client->dev, indio_dev);
199df041e73SPuranjay Mohan }
200df041e73SPuranjay Mohan 
201df041e73SPuranjay Mohan static const struct of_device_id tmp117_of_match[] = {
2020cd2889dSMarco Felsch 	{ .compatible = "ti,tmp116", .data = (void *)TMP116_DEVICE_ID },
20307cc6899SMarco Felsch 	{ .compatible = "ti,tmp117", .data = (void *)TMP117_DEVICE_ID },
204df041e73SPuranjay Mohan 	{ }
205df041e73SPuranjay Mohan };
206df041e73SPuranjay Mohan MODULE_DEVICE_TABLE(of, tmp117_of_match);
207df041e73SPuranjay Mohan 
208df041e73SPuranjay Mohan static const struct i2c_device_id tmp117_id[] = {
2090cd2889dSMarco Felsch 	{ "tmp116", TMP116_DEVICE_ID },
21007cc6899SMarco Felsch 	{ "tmp117", TMP117_DEVICE_ID },
211df041e73SPuranjay Mohan 	{ }
212df041e73SPuranjay Mohan };
213df041e73SPuranjay Mohan MODULE_DEVICE_TABLE(i2c, tmp117_id);
214df041e73SPuranjay Mohan 
215df041e73SPuranjay Mohan static struct i2c_driver tmp117_driver = {
216df041e73SPuranjay Mohan 	.driver = {
217df041e73SPuranjay Mohan 		.name	= "tmp117",
218df041e73SPuranjay Mohan 		.of_match_table = tmp117_of_match,
219df041e73SPuranjay Mohan 	},
220df041e73SPuranjay Mohan 	.probe		= tmp117_probe,
221df041e73SPuranjay Mohan 	.id_table	= tmp117_id,
222df041e73SPuranjay Mohan };
223df041e73SPuranjay Mohan module_i2c_driver(tmp117_driver);
224df041e73SPuranjay Mohan 
225df041e73SPuranjay Mohan MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>");
226df041e73SPuranjay Mohan MODULE_DESCRIPTION("TI TMP117 Temperature sensor driver");
227df041e73SPuranjay Mohan MODULE_LICENSE("GPL");
228