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