xref: /openbmc/linux/drivers/iio/temperature/tsys01.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
243e53407SLudovic Tancerel /*
343e53407SLudovic Tancerel  * tsys01.c - Support for Measurement-Specialties tsys01 temperature sensor
443e53407SLudovic Tancerel  *
543e53407SLudovic Tancerel  * Copyright (c) 2015 Measurement-Specialties
643e53407SLudovic Tancerel  *
743e53407SLudovic Tancerel  * Datasheet:
843e53407SLudovic Tancerel  *  http://www.meas-spec.com/downloads/TSYS01_Digital_Temperature_Sensor.pdf
943e53407SLudovic Tancerel  */
1043e53407SLudovic Tancerel 
1143e53407SLudovic Tancerel #include <linux/iio/iio.h>
1243e53407SLudovic Tancerel #include <linux/iio/sysfs.h>
1343e53407SLudovic Tancerel #include <linux/device.h>
1443e53407SLudovic Tancerel #include <linux/mutex.h>
1543e53407SLudovic Tancerel #include <linux/module.h>
16c5b411bcSJonathan Cameron #include <linux/mod_devicetable.h>
1743e53407SLudovic Tancerel #include <linux/init.h>
1843e53407SLudovic Tancerel #include <linux/kernel.h>
1943e53407SLudovic Tancerel #include <linux/stat.h>
2043e53407SLudovic Tancerel #include "../common/ms_sensors/ms_sensors_i2c.h"
2143e53407SLudovic Tancerel 
2243e53407SLudovic Tancerel /* TSYS01 Commands */
2343e53407SLudovic Tancerel #define TSYS01_RESET				0x1E
2443e53407SLudovic Tancerel #define TSYS01_CONVERSION_START			0x48
2543e53407SLudovic Tancerel #define TSYS01_ADC_READ				0x00
2643e53407SLudovic Tancerel #define TSYS01_PROM_READ			0xA0
2743e53407SLudovic Tancerel 
2843e53407SLudovic Tancerel #define TSYS01_PROM_WORDS_NB			8
2943e53407SLudovic Tancerel 
3043e53407SLudovic Tancerel struct tsys01_dev {
3143e53407SLudovic Tancerel 	void *client;
3243e53407SLudovic Tancerel 	struct mutex lock; /* lock during conversion */
3343e53407SLudovic Tancerel 
3443e53407SLudovic Tancerel 	int (*reset)(void *cli, u8 cmd, unsigned int delay);
3543e53407SLudovic Tancerel 	int (*convert_and_read)(void *cli, u8 conv, u8 rd,
3643e53407SLudovic Tancerel 				unsigned int delay, u32 *adc);
3743e53407SLudovic Tancerel 	int (*read_prom_word)(void *cli, int cmd, u16 *word);
3843e53407SLudovic Tancerel 
3943e53407SLudovic Tancerel 	u16 prom[TSYS01_PROM_WORDS_NB];
4043e53407SLudovic Tancerel };
4143e53407SLudovic Tancerel 
4243e53407SLudovic Tancerel /* Multiplication coefficients for temperature computation */
4343e53407SLudovic Tancerel static const int coeff_mul[] = { -1500000, 1000000, -2000000,
4443e53407SLudovic Tancerel 				 4000000, -2000000 };
4543e53407SLudovic Tancerel 
tsys01_read_temperature(struct iio_dev * indio_dev,s32 * temperature)4643e53407SLudovic Tancerel static int tsys01_read_temperature(struct iio_dev *indio_dev,
4743e53407SLudovic Tancerel 				   s32 *temperature)
4843e53407SLudovic Tancerel {
4943e53407SLudovic Tancerel 	int ret, i;
5043e53407SLudovic Tancerel 	u32 adc;
5143e53407SLudovic Tancerel 	s64 temp = 0;
5243e53407SLudovic Tancerel 	struct tsys01_dev *dev_data = iio_priv(indio_dev);
5343e53407SLudovic Tancerel 
5443e53407SLudovic Tancerel 	mutex_lock(&dev_data->lock);
5543e53407SLudovic Tancerel 	ret = dev_data->convert_and_read(dev_data->client,
5643e53407SLudovic Tancerel 					 TSYS01_CONVERSION_START,
5743e53407SLudovic Tancerel 					 TSYS01_ADC_READ, 9000, &adc);
5843e53407SLudovic Tancerel 	mutex_unlock(&dev_data->lock);
5943e53407SLudovic Tancerel 	if (ret)
6043e53407SLudovic Tancerel 		return ret;
6143e53407SLudovic Tancerel 
6243e53407SLudovic Tancerel 	adc >>= 8;
6343e53407SLudovic Tancerel 
6443e53407SLudovic Tancerel 	/* Temperature algorithm */
6543e53407SLudovic Tancerel 	for (i = 4; i > 0; i--) {
6643e53407SLudovic Tancerel 		temp += coeff_mul[i] *
6743e53407SLudovic Tancerel 			(s64)dev_data->prom[5 - i];
6843e53407SLudovic Tancerel 		temp *= (s64)adc;
6943e53407SLudovic Tancerel 		temp = div64_s64(temp, 100000);
7043e53407SLudovic Tancerel 	}
7143e53407SLudovic Tancerel 	temp *= 10;
7243e53407SLudovic Tancerel 	temp += coeff_mul[0] * (s64)dev_data->prom[5];
7343e53407SLudovic Tancerel 	temp = div64_s64(temp, 100000);
7443e53407SLudovic Tancerel 
7543e53407SLudovic Tancerel 	*temperature = temp;
7643e53407SLudovic Tancerel 
7743e53407SLudovic Tancerel 	return 0;
7843e53407SLudovic Tancerel }
7943e53407SLudovic Tancerel 
tsys01_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * channel,int * val,int * val2,long mask)8043e53407SLudovic Tancerel static int tsys01_read_raw(struct iio_dev *indio_dev,
8143e53407SLudovic Tancerel 			   struct iio_chan_spec const *channel, int *val,
8243e53407SLudovic Tancerel 			   int *val2, long mask)
8343e53407SLudovic Tancerel {
8443e53407SLudovic Tancerel 	int ret;
8543e53407SLudovic Tancerel 	s32 temperature;
8643e53407SLudovic Tancerel 
8743e53407SLudovic Tancerel 	switch (mask) {
8843e53407SLudovic Tancerel 	case IIO_CHAN_INFO_PROCESSED:
8943e53407SLudovic Tancerel 		switch (channel->type) {
9043e53407SLudovic Tancerel 		case IIO_TEMP:	/* in milli °C */
9143e53407SLudovic Tancerel 			ret = tsys01_read_temperature(indio_dev, &temperature);
9243e53407SLudovic Tancerel 			if (ret)
9343e53407SLudovic Tancerel 				return ret;
9443e53407SLudovic Tancerel 			*val = temperature;
9543e53407SLudovic Tancerel 
9643e53407SLudovic Tancerel 			return IIO_VAL_INT;
9743e53407SLudovic Tancerel 		default:
9843e53407SLudovic Tancerel 			return -EINVAL;
9943e53407SLudovic Tancerel 		}
10043e53407SLudovic Tancerel 	default:
10143e53407SLudovic Tancerel 		return -EINVAL;
10243e53407SLudovic Tancerel 	}
10343e53407SLudovic Tancerel }
10443e53407SLudovic Tancerel 
10543e53407SLudovic Tancerel static const struct iio_chan_spec tsys01_channels[] = {
10643e53407SLudovic Tancerel 	{
10743e53407SLudovic Tancerel 		.type = IIO_TEMP,
10843e53407SLudovic Tancerel 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
10943e53407SLudovic Tancerel 	}
11043e53407SLudovic Tancerel };
11143e53407SLudovic Tancerel 
11243e53407SLudovic Tancerel static const struct iio_info tsys01_info = {
11343e53407SLudovic Tancerel 	.read_raw = tsys01_read_raw,
11443e53407SLudovic Tancerel };
11543e53407SLudovic Tancerel 
tsys01_crc_valid(u16 * n_prom)11643e53407SLudovic Tancerel static bool tsys01_crc_valid(u16 *n_prom)
11743e53407SLudovic Tancerel {
11843e53407SLudovic Tancerel 	u8 cnt;
11943e53407SLudovic Tancerel 	u8 sum = 0;
12043e53407SLudovic Tancerel 
12143e53407SLudovic Tancerel 	for (cnt = 0; cnt < TSYS01_PROM_WORDS_NB; cnt++)
12243e53407SLudovic Tancerel 		sum += ((n_prom[0] >> 8) + (n_prom[0] & 0xFF));
12343e53407SLudovic Tancerel 
12443e53407SLudovic Tancerel 	return (sum == 0);
12543e53407SLudovic Tancerel }
12643e53407SLudovic Tancerel 
tsys01_read_prom(struct iio_dev * indio_dev)12743e53407SLudovic Tancerel static int tsys01_read_prom(struct iio_dev *indio_dev)
12843e53407SLudovic Tancerel {
12943e53407SLudovic Tancerel 	int i, ret;
13043e53407SLudovic Tancerel 	struct tsys01_dev *dev_data = iio_priv(indio_dev);
13143e53407SLudovic Tancerel 	char buf[7 * TSYS01_PROM_WORDS_NB + 1];
13243e53407SLudovic Tancerel 	char *ptr = buf;
13343e53407SLudovic Tancerel 
13443e53407SLudovic Tancerel 	for (i = 0; i < TSYS01_PROM_WORDS_NB; i++) {
13543e53407SLudovic Tancerel 		ret = dev_data->read_prom_word(dev_data->client,
13643e53407SLudovic Tancerel 					       TSYS01_PROM_READ + (i << 1),
13743e53407SLudovic Tancerel 					       &dev_data->prom[i]);
13843e53407SLudovic Tancerel 		if (ret)
13943e53407SLudovic Tancerel 			return ret;
14043e53407SLudovic Tancerel 
14143e53407SLudovic Tancerel 		ret = sprintf(ptr, "0x%04x ", dev_data->prom[i]);
14243e53407SLudovic Tancerel 		ptr += ret;
14343e53407SLudovic Tancerel 	}
14443e53407SLudovic Tancerel 
14543e53407SLudovic Tancerel 	if (!tsys01_crc_valid(dev_data->prom)) {
14643e53407SLudovic Tancerel 		dev_err(&indio_dev->dev, "prom crc check error\n");
14743e53407SLudovic Tancerel 		return -ENODEV;
14843e53407SLudovic Tancerel 	}
14943e53407SLudovic Tancerel 	*ptr = 0;
15043e53407SLudovic Tancerel 	dev_info(&indio_dev->dev, "PROM coefficients : %s\n", buf);
15143e53407SLudovic Tancerel 
15243e53407SLudovic Tancerel 	return 0;
15343e53407SLudovic Tancerel }
15443e53407SLudovic Tancerel 
tsys01_probe(struct iio_dev * indio_dev,struct device * dev)15543e53407SLudovic Tancerel static int tsys01_probe(struct iio_dev *indio_dev, struct device *dev)
15643e53407SLudovic Tancerel {
15743e53407SLudovic Tancerel 	int ret;
15843e53407SLudovic Tancerel 	struct tsys01_dev *dev_data = iio_priv(indio_dev);
15943e53407SLudovic Tancerel 
16043e53407SLudovic Tancerel 	mutex_init(&dev_data->lock);
16143e53407SLudovic Tancerel 
16243e53407SLudovic Tancerel 	indio_dev->info = &tsys01_info;
16343e53407SLudovic Tancerel 	indio_dev->name = dev->driver->name;
16443e53407SLudovic Tancerel 	indio_dev->modes = INDIO_DIRECT_MODE;
16543e53407SLudovic Tancerel 	indio_dev->channels = tsys01_channels;
16643e53407SLudovic Tancerel 	indio_dev->num_channels = ARRAY_SIZE(tsys01_channels);
16743e53407SLudovic Tancerel 
16843e53407SLudovic Tancerel 	ret = dev_data->reset(dev_data->client, TSYS01_RESET, 3000);
16943e53407SLudovic Tancerel 	if (ret)
17043e53407SLudovic Tancerel 		return ret;
17143e53407SLudovic Tancerel 
17243e53407SLudovic Tancerel 	ret = tsys01_read_prom(indio_dev);
17343e53407SLudovic Tancerel 	if (ret)
17443e53407SLudovic Tancerel 		return ret;
17543e53407SLudovic Tancerel 
17643e53407SLudovic Tancerel 	return devm_iio_device_register(dev, indio_dev);
17743e53407SLudovic Tancerel }
17843e53407SLudovic Tancerel 
tsys01_i2c_probe(struct i2c_client * client)179d7c94228SUwe Kleine-König static int tsys01_i2c_probe(struct i2c_client *client)
18043e53407SLudovic Tancerel {
18143e53407SLudovic Tancerel 	struct tsys01_dev *dev_data;
18243e53407SLudovic Tancerel 	struct iio_dev *indio_dev;
18343e53407SLudovic Tancerel 
18443e53407SLudovic Tancerel 	if (!i2c_check_functionality(client->adapter,
18543e53407SLudovic Tancerel 				     I2C_FUNC_SMBUS_WORD_DATA |
18643e53407SLudovic Tancerel 				     I2C_FUNC_SMBUS_WRITE_BYTE |
18743e53407SLudovic Tancerel 				     I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
18843e53407SLudovic Tancerel 		dev_err(&client->dev,
18943e53407SLudovic Tancerel 			"Adapter does not support some i2c transaction\n");
190f8d9d3b4SMatt Ranostay 		return -EOPNOTSUPP;
19143e53407SLudovic Tancerel 	}
19243e53407SLudovic Tancerel 
19343e53407SLudovic Tancerel 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
19443e53407SLudovic Tancerel 	if (!indio_dev)
19543e53407SLudovic Tancerel 		return -ENOMEM;
19643e53407SLudovic Tancerel 
19743e53407SLudovic Tancerel 	dev_data = iio_priv(indio_dev);
19843e53407SLudovic Tancerel 	dev_data->client = client;
19943e53407SLudovic Tancerel 	dev_data->reset = ms_sensors_reset;
20043e53407SLudovic Tancerel 	dev_data->read_prom_word = ms_sensors_read_prom_word;
20143e53407SLudovic Tancerel 	dev_data->convert_and_read = ms_sensors_convert_and_read;
20243e53407SLudovic Tancerel 
20343e53407SLudovic Tancerel 	i2c_set_clientdata(client, indio_dev);
20443e53407SLudovic Tancerel 
20543e53407SLudovic Tancerel 	return tsys01_probe(indio_dev, &client->dev);
20643e53407SLudovic Tancerel }
20743e53407SLudovic Tancerel 
20843e53407SLudovic Tancerel static const struct i2c_device_id tsys01_id[] = {
20943e53407SLudovic Tancerel 	{"tsys01", 0},
21043e53407SLudovic Tancerel 	{}
21143e53407SLudovic Tancerel };
21243e53407SLudovic Tancerel MODULE_DEVICE_TABLE(i2c, tsys01_id);
21343e53407SLudovic Tancerel 
21455eaac2bSManivannan Sadhasivam static const struct of_device_id tsys01_of_match[] = {
21555eaac2bSManivannan Sadhasivam 	{ .compatible = "meas,tsys01", },
21655eaac2bSManivannan Sadhasivam 	{ },
21755eaac2bSManivannan Sadhasivam };
21855eaac2bSManivannan Sadhasivam MODULE_DEVICE_TABLE(of, tsys01_of_match);
21955eaac2bSManivannan Sadhasivam 
22043e53407SLudovic Tancerel static struct i2c_driver tsys01_driver = {
221*7cf15f42SUwe Kleine-König 	.probe = tsys01_i2c_probe,
22243e53407SLudovic Tancerel 	.id_table = tsys01_id,
22343e53407SLudovic Tancerel 	.driver = {
22443e53407SLudovic Tancerel 		   .name = "tsys01",
225c5b411bcSJonathan Cameron 		   .of_match_table = tsys01_of_match,
22643e53407SLudovic Tancerel 		   },
22743e53407SLudovic Tancerel };
22843e53407SLudovic Tancerel 
22943e53407SLudovic Tancerel module_i2c_driver(tsys01_driver);
23043e53407SLudovic Tancerel 
23143e53407SLudovic Tancerel MODULE_DESCRIPTION("Measurement-Specialties tsys01 temperature driver");
23243e53407SLudovic Tancerel MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
23343e53407SLudovic Tancerel MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
23443e53407SLudovic Tancerel MODULE_LICENSE("GPL v2");
235a7f6cecfSJonathan Cameron MODULE_IMPORT_NS(IIO_MEAS_SPEC_SENSORS);
236