xref: /openbmc/linux/drivers/iio/chemical/sgp40.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
11081b9d9SAndreas Klinger // SPDX-License-Identifier: GPL-2.0+
21081b9d9SAndreas Klinger /*
31081b9d9SAndreas Klinger  * sgp40.c - Support for Sensirion SGP40 Gas Sensor
41081b9d9SAndreas Klinger  *
51081b9d9SAndreas Klinger  * Copyright (C) 2021 Andreas Klinger <ak@it-klinger.de>
61081b9d9SAndreas Klinger  *
71081b9d9SAndreas Klinger  * I2C slave address: 0x59
81081b9d9SAndreas Klinger  *
91081b9d9SAndreas Klinger  * Datasheet can be found here:
101081b9d9SAndreas Klinger  * https://www.sensirion.com/file/datasheet_sgp40
111081b9d9SAndreas Klinger  *
121081b9d9SAndreas Klinger  * There are two functionalities supported:
131081b9d9SAndreas Klinger  *
141081b9d9SAndreas Klinger  * 1) read raw logarithmic resistance value from sensor
151081b9d9SAndreas Klinger  *    --> useful to pass it to the algorithm of the sensor vendor for
161081b9d9SAndreas Klinger  *    measuring deteriorations and improvements of air quality.
171081b9d9SAndreas Klinger  *
181081b9d9SAndreas Klinger  * 2) calculate an estimated absolute voc index (0 - 500 index points) for
191081b9d9SAndreas Klinger  *    measuring the air quality.
201081b9d9SAndreas Klinger  *    For this purpose the value of the resistance for which the voc index
211081b9d9SAndreas Klinger  *    will be 250 can be set up using calibbias.
221081b9d9SAndreas Klinger  *
231081b9d9SAndreas Klinger  * Compensation values of relative humidity and temperature can be set up
241081b9d9SAndreas Klinger  * by writing to the out values of temp and humidityrelative.
251081b9d9SAndreas Klinger  */
261081b9d9SAndreas Klinger 
271081b9d9SAndreas Klinger #include <linux/delay.h>
281081b9d9SAndreas Klinger #include <linux/crc8.h>
291081b9d9SAndreas Klinger #include <linux/module.h>
301081b9d9SAndreas Klinger #include <linux/mutex.h>
311081b9d9SAndreas Klinger #include <linux/i2c.h>
321081b9d9SAndreas Klinger #include <linux/iio/iio.h>
331081b9d9SAndreas Klinger 
341081b9d9SAndreas Klinger /*
351081b9d9SAndreas Klinger  * floating point calculation of voc is done as integer
361081b9d9SAndreas Klinger  * where numbers are multiplied by 1 << SGP40_CALC_POWER
371081b9d9SAndreas Klinger  */
381081b9d9SAndreas Klinger #define SGP40_CALC_POWER	14
391081b9d9SAndreas Klinger 
401081b9d9SAndreas Klinger #define SGP40_CRC8_POLYNOMIAL	0x31
411081b9d9SAndreas Klinger #define SGP40_CRC8_INIT		0xff
421081b9d9SAndreas Klinger 
431081b9d9SAndreas Klinger DECLARE_CRC8_TABLE(sgp40_crc8_table);
441081b9d9SAndreas Klinger 
451081b9d9SAndreas Klinger struct sgp40_data {
461081b9d9SAndreas Klinger 	struct device		*dev;
471081b9d9SAndreas Klinger 	struct i2c_client	*client;
481081b9d9SAndreas Klinger 	int			rht;
491081b9d9SAndreas Klinger 	int			temp;
501081b9d9SAndreas Klinger 	int			res_calibbias;
511081b9d9SAndreas Klinger 	/* Prevent concurrent access to rht, tmp, calibbias */
521081b9d9SAndreas Klinger 	struct mutex		lock;
531081b9d9SAndreas Klinger };
541081b9d9SAndreas Klinger 
551081b9d9SAndreas Klinger struct sgp40_tg_measure {
561081b9d9SAndreas Klinger 	u8	command[2];
571081b9d9SAndreas Klinger 	__be16	rht_ticks;
581081b9d9SAndreas Klinger 	u8	rht_crc;
591081b9d9SAndreas Klinger 	__be16	temp_ticks;
601081b9d9SAndreas Klinger 	u8	temp_crc;
611081b9d9SAndreas Klinger } __packed;
621081b9d9SAndreas Klinger 
631081b9d9SAndreas Klinger struct sgp40_tg_result {
641081b9d9SAndreas Klinger 	__be16	res_ticks;
651081b9d9SAndreas Klinger 	u8	res_crc;
661081b9d9SAndreas Klinger } __packed;
671081b9d9SAndreas Klinger 
681081b9d9SAndreas Klinger static const struct iio_chan_spec sgp40_channels[] = {
691081b9d9SAndreas Klinger 	{
701081b9d9SAndreas Klinger 		.type = IIO_CONCENTRATION,
711081b9d9SAndreas Klinger 		.channel2 = IIO_MOD_VOC,
721081b9d9SAndreas Klinger 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
731081b9d9SAndreas Klinger 	},
741081b9d9SAndreas Klinger 	{
751081b9d9SAndreas Klinger 		.type = IIO_RESISTANCE,
761081b9d9SAndreas Klinger 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
771081b9d9SAndreas Klinger 			BIT(IIO_CHAN_INFO_CALIBBIAS),
781081b9d9SAndreas Klinger 	},
791081b9d9SAndreas Klinger 	{
801081b9d9SAndreas Klinger 		.type = IIO_TEMP,
811081b9d9SAndreas Klinger 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
821081b9d9SAndreas Klinger 		.output = 1,
831081b9d9SAndreas Klinger 	},
841081b9d9SAndreas Klinger 	{
851081b9d9SAndreas Klinger 		.type = IIO_HUMIDITYRELATIVE,
861081b9d9SAndreas Klinger 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
871081b9d9SAndreas Klinger 		.output = 1,
881081b9d9SAndreas Klinger 	},
891081b9d9SAndreas Klinger };
901081b9d9SAndreas Klinger 
911081b9d9SAndreas Klinger /*
921081b9d9SAndreas Klinger  * taylor approximation of e^x:
931081b9d9SAndreas Klinger  * y = 1 + x + x^2 / 2 + x^3 / 6 + x^4 / 24 + ... + x^n / n!
941081b9d9SAndreas Klinger  *
951081b9d9SAndreas Klinger  * Because we are calculating x real value multiplied by 2^power we get
961081b9d9SAndreas Klinger  * an additional 2^power^n to divide for every element. For a reasonable
971081b9d9SAndreas Klinger  * precision this would overflow after a few iterations. Therefore we
981081b9d9SAndreas Klinger  * divide the x^n part whenever its about to overflow (xmax).
991081b9d9SAndreas Klinger  */
1001081b9d9SAndreas Klinger 
sgp40_exp(int exp,u32 power,u32 rounds)1011081b9d9SAndreas Klinger static u32 sgp40_exp(int exp, u32 power, u32 rounds)
1021081b9d9SAndreas Klinger {
1031081b9d9SAndreas Klinger         u32 x, y, xp;
1041081b9d9SAndreas Klinger         u32 factorial, divider, xmax;
1051081b9d9SAndreas Klinger         int sign = 1;
1061081b9d9SAndreas Klinger 	int i;
1071081b9d9SAndreas Klinger 
1081081b9d9SAndreas Klinger         if (exp == 0)
1091081b9d9SAndreas Klinger                 return 1 << power;
1101081b9d9SAndreas Klinger         else if (exp < 0) {
1111081b9d9SAndreas Klinger                 sign = -1;
1121081b9d9SAndreas Klinger                 exp *= -1;
1131081b9d9SAndreas Klinger         }
1141081b9d9SAndreas Klinger 
1151081b9d9SAndreas Klinger         xmax = 0x7FFFFFFF / exp;
1161081b9d9SAndreas Klinger         x = exp;
1171081b9d9SAndreas Klinger         xp = 1;
1181081b9d9SAndreas Klinger         factorial = 1;
1191081b9d9SAndreas Klinger         y = 1 << power;
1201081b9d9SAndreas Klinger         divider = 0;
1211081b9d9SAndreas Klinger 
1221081b9d9SAndreas Klinger         for (i = 1; i <= rounds; i++) {
1231081b9d9SAndreas Klinger                 xp *= x;
1241081b9d9SAndreas Klinger                 factorial *= i;
1251081b9d9SAndreas Klinger                 y += (xp >> divider) / factorial;
1261081b9d9SAndreas Klinger                 divider += power;
1271081b9d9SAndreas Klinger                 /* divide when next multiplication would overflow */
1281081b9d9SAndreas Klinger                 if (xp >= xmax) {
1291081b9d9SAndreas Klinger                         xp >>= power;
1301081b9d9SAndreas Klinger                         divider -= power;
1311081b9d9SAndreas Klinger                 }
1321081b9d9SAndreas Klinger         }
1331081b9d9SAndreas Klinger 
1341081b9d9SAndreas Klinger         if (sign == -1)
1351081b9d9SAndreas Klinger                 return (1 << (power * 2)) / y;
1361081b9d9SAndreas Klinger         else
1371081b9d9SAndreas Klinger                 return y;
1381081b9d9SAndreas Klinger }
1391081b9d9SAndreas Klinger 
sgp40_calc_voc(struct sgp40_data * data,u16 resistance_raw,int * voc)1401081b9d9SAndreas Klinger static int sgp40_calc_voc(struct sgp40_data *data, u16 resistance_raw, int *voc)
1411081b9d9SAndreas Klinger {
1421081b9d9SAndreas Klinger 	int x;
1431081b9d9SAndreas Klinger 	u32 exp = 0;
1441081b9d9SAndreas Klinger 
1451081b9d9SAndreas Klinger 	/* we calculate as a multiple of 16384 (2^14) */
1461081b9d9SAndreas Klinger 	mutex_lock(&data->lock);
1471081b9d9SAndreas Klinger 	x = ((int)resistance_raw - data->res_calibbias) * 106;
1481081b9d9SAndreas Klinger 	mutex_unlock(&data->lock);
1491081b9d9SAndreas Klinger 
1501081b9d9SAndreas Klinger 	/* voc = 500 / (1 + e^x) */
1511081b9d9SAndreas Klinger 	exp = sgp40_exp(x, SGP40_CALC_POWER, 18);
1521081b9d9SAndreas Klinger 	*voc = 500 * ((1 << (SGP40_CALC_POWER * 2)) / ((1<<SGP40_CALC_POWER) + exp));
1531081b9d9SAndreas Klinger 
1541081b9d9SAndreas Klinger 	dev_dbg(data->dev, "raw: %d res_calibbias: %d x: %d exp: %d voc: %d\n",
1551081b9d9SAndreas Klinger 				resistance_raw, data->res_calibbias, x, exp, *voc);
1561081b9d9SAndreas Klinger 
1571081b9d9SAndreas Klinger 	return 0;
1581081b9d9SAndreas Klinger }
1591081b9d9SAndreas Klinger 
sgp40_measure_resistance_raw(struct sgp40_data * data,u16 * resistance_raw)1601081b9d9SAndreas Klinger static int sgp40_measure_resistance_raw(struct sgp40_data *data, u16 *resistance_raw)
1611081b9d9SAndreas Klinger {
1621081b9d9SAndreas Klinger 	int ret;
1631081b9d9SAndreas Klinger 	struct i2c_client *client = data->client;
1641081b9d9SAndreas Klinger 	u32 ticks;
1651081b9d9SAndreas Klinger 	u16 ticks16;
1661081b9d9SAndreas Klinger 	u8 crc;
1671081b9d9SAndreas Klinger 	struct sgp40_tg_measure tg = {.command = {0x26, 0x0F}};
1681081b9d9SAndreas Klinger 	struct sgp40_tg_result tgres;
1691081b9d9SAndreas Klinger 
1701081b9d9SAndreas Klinger 	mutex_lock(&data->lock);
1711081b9d9SAndreas Klinger 
1721081b9d9SAndreas Klinger 	ticks = (data->rht / 10) * 65535 / 10000;
1731081b9d9SAndreas Klinger 	ticks16 = (u16)clamp(ticks, 0u, 65535u); /* clamp between 0 .. 100 %rH */
1741081b9d9SAndreas Klinger 	tg.rht_ticks = cpu_to_be16(ticks16);
1751081b9d9SAndreas Klinger 	tg.rht_crc = crc8(sgp40_crc8_table, (u8 *)&tg.rht_ticks, 2, SGP40_CRC8_INIT);
1761081b9d9SAndreas Klinger 
1771081b9d9SAndreas Klinger 	ticks = ((data->temp + 45000) / 10 ) * 65535 / 17500;
1781081b9d9SAndreas Klinger 	ticks16 = (u16)clamp(ticks, 0u, 65535u); /* clamp between -45 .. +130 °C */
1791081b9d9SAndreas Klinger 	tg.temp_ticks = cpu_to_be16(ticks16);
1801081b9d9SAndreas Klinger 	tg.temp_crc = crc8(sgp40_crc8_table, (u8 *)&tg.temp_ticks, 2, SGP40_CRC8_INIT);
1811081b9d9SAndreas Klinger 
1821081b9d9SAndreas Klinger 	mutex_unlock(&data->lock);
1831081b9d9SAndreas Klinger 
1841081b9d9SAndreas Klinger 	ret = i2c_master_send(client, (const char *)&tg, sizeof(tg));
1851081b9d9SAndreas Klinger 	if (ret != sizeof(tg)) {
1861081b9d9SAndreas Klinger 		dev_warn(data->dev, "i2c_master_send ret: %d sizeof: %zu\n", ret, sizeof(tg));
1871081b9d9SAndreas Klinger 		return -EIO;
1881081b9d9SAndreas Klinger 	}
1891081b9d9SAndreas Klinger 	msleep(30);
1901081b9d9SAndreas Klinger 
1911081b9d9SAndreas Klinger 	ret = i2c_master_recv(client, (u8 *)&tgres, sizeof(tgres));
1921081b9d9SAndreas Klinger 	if (ret < 0)
1931081b9d9SAndreas Klinger 		return ret;
1941081b9d9SAndreas Klinger 	if (ret != sizeof(tgres)) {
1951081b9d9SAndreas Klinger 		dev_warn(data->dev, "i2c_master_recv ret: %d sizeof: %zu\n", ret, sizeof(tgres));
1961081b9d9SAndreas Klinger 		return -EIO;
1971081b9d9SAndreas Klinger 	}
1981081b9d9SAndreas Klinger 
1991081b9d9SAndreas Klinger 	crc = crc8(sgp40_crc8_table, (u8 *)&tgres.res_ticks, 2, SGP40_CRC8_INIT);
2001081b9d9SAndreas Klinger 	if (crc != tgres.res_crc) {
2011081b9d9SAndreas Klinger 		dev_err(data->dev, "CRC error while measure-raw\n");
2021081b9d9SAndreas Klinger 		return -EIO;
2031081b9d9SAndreas Klinger 	}
2041081b9d9SAndreas Klinger 
2051081b9d9SAndreas Klinger 	*resistance_raw = be16_to_cpu(tgres.res_ticks);
2061081b9d9SAndreas Klinger 
2071081b9d9SAndreas Klinger 	return 0;
2081081b9d9SAndreas Klinger }
2091081b9d9SAndreas Klinger 
sgp40_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)2101081b9d9SAndreas Klinger static int sgp40_read_raw(struct iio_dev *indio_dev,
2111081b9d9SAndreas Klinger 			struct iio_chan_spec const *chan, int *val,
2121081b9d9SAndreas Klinger 			int *val2, long mask)
2131081b9d9SAndreas Klinger {
2141081b9d9SAndreas Klinger 	struct sgp40_data *data = iio_priv(indio_dev);
2151081b9d9SAndreas Klinger 	int ret, voc;
2161081b9d9SAndreas Klinger 	u16 resistance_raw;
2171081b9d9SAndreas Klinger 
2181081b9d9SAndreas Klinger 	switch (mask) {
2191081b9d9SAndreas Klinger 	case IIO_CHAN_INFO_RAW:
2201081b9d9SAndreas Klinger 		switch (chan->type) {
2211081b9d9SAndreas Klinger 		case IIO_RESISTANCE:
2221081b9d9SAndreas Klinger 			ret = sgp40_measure_resistance_raw(data, &resistance_raw);
2231081b9d9SAndreas Klinger 			if (ret)
2241081b9d9SAndreas Klinger 				return ret;
2251081b9d9SAndreas Klinger 
2261081b9d9SAndreas Klinger 			*val = resistance_raw;
2271081b9d9SAndreas Klinger 			return IIO_VAL_INT;
2281081b9d9SAndreas Klinger 		case IIO_TEMP:
2291081b9d9SAndreas Klinger 			mutex_lock(&data->lock);
2301081b9d9SAndreas Klinger 			*val = data->temp;
2311081b9d9SAndreas Klinger 			mutex_unlock(&data->lock);
2321081b9d9SAndreas Klinger 			return IIO_VAL_INT;
2331081b9d9SAndreas Klinger 		case IIO_HUMIDITYRELATIVE:
2341081b9d9SAndreas Klinger 			mutex_lock(&data->lock);
2351081b9d9SAndreas Klinger 			*val = data->rht;
2361081b9d9SAndreas Klinger 			mutex_unlock(&data->lock);
2371081b9d9SAndreas Klinger 			return IIO_VAL_INT;
2381081b9d9SAndreas Klinger 		default:
2391081b9d9SAndreas Klinger 			return -EINVAL;
2401081b9d9SAndreas Klinger 		}
2411081b9d9SAndreas Klinger 	case IIO_CHAN_INFO_PROCESSED:
2421081b9d9SAndreas Klinger 		ret = sgp40_measure_resistance_raw(data, &resistance_raw);
2431081b9d9SAndreas Klinger 		if (ret)
2441081b9d9SAndreas Klinger 			return ret;
2451081b9d9SAndreas Klinger 
2461081b9d9SAndreas Klinger 		ret = sgp40_calc_voc(data, resistance_raw, &voc);
2471081b9d9SAndreas Klinger 		if (ret)
2481081b9d9SAndreas Klinger 			return ret;
2491081b9d9SAndreas Klinger 
2501081b9d9SAndreas Klinger 		*val = voc / (1 << SGP40_CALC_POWER);
2511081b9d9SAndreas Klinger 		/*
2521081b9d9SAndreas Klinger 		 * calculation should fit into integer, where:
2531081b9d9SAndreas Klinger 		 * voc <= (500 * 2^SGP40_CALC_POWER) = 8192000
2541081b9d9SAndreas Klinger 		 * (with SGP40_CALC_POWER = 14)
2551081b9d9SAndreas Klinger 		 */
2561081b9d9SAndreas Klinger 		*val2 = ((voc % (1 << SGP40_CALC_POWER)) * 244) / (1 << (SGP40_CALC_POWER - 12));
2571081b9d9SAndreas Klinger 		dev_dbg(data->dev, "voc: %d val: %d.%06d\n", voc, *val, *val2);
2581081b9d9SAndreas Klinger 		return IIO_VAL_INT_PLUS_MICRO;
2591081b9d9SAndreas Klinger 	case IIO_CHAN_INFO_CALIBBIAS:
2601081b9d9SAndreas Klinger 		mutex_lock(&data->lock);
2611081b9d9SAndreas Klinger 		*val = data->res_calibbias;
2621081b9d9SAndreas Klinger 		mutex_unlock(&data->lock);
2631081b9d9SAndreas Klinger 		return IIO_VAL_INT;
2641081b9d9SAndreas Klinger 	default:
2651081b9d9SAndreas Klinger 		return -EINVAL;
2661081b9d9SAndreas Klinger 	}
2671081b9d9SAndreas Klinger }
2681081b9d9SAndreas Klinger 
sgp40_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)2691081b9d9SAndreas Klinger static int sgp40_write_raw(struct iio_dev *indio_dev,
2701081b9d9SAndreas Klinger 			struct iio_chan_spec const *chan, int val,
2711081b9d9SAndreas Klinger 			int val2, long mask)
2721081b9d9SAndreas Klinger {
2731081b9d9SAndreas Klinger 	struct sgp40_data *data = iio_priv(indio_dev);
2741081b9d9SAndreas Klinger 
2751081b9d9SAndreas Klinger 	switch (mask) {
2761081b9d9SAndreas Klinger 	case IIO_CHAN_INFO_RAW:
2771081b9d9SAndreas Klinger 		switch (chan->type) {
2781081b9d9SAndreas Klinger 		case IIO_TEMP:
2791081b9d9SAndreas Klinger 			if ((val < -45000) || (val > 130000))
2801081b9d9SAndreas Klinger 				return -EINVAL;
2811081b9d9SAndreas Klinger 
2821081b9d9SAndreas Klinger 			mutex_lock(&data->lock);
2831081b9d9SAndreas Klinger 			data->temp = val;
2841081b9d9SAndreas Klinger 			mutex_unlock(&data->lock);
2851081b9d9SAndreas Klinger 			return 0;
2861081b9d9SAndreas Klinger 		case IIO_HUMIDITYRELATIVE:
2871081b9d9SAndreas Klinger 			if ((val < 0) || (val > 100000))
2881081b9d9SAndreas Klinger 				return -EINVAL;
2891081b9d9SAndreas Klinger 
2901081b9d9SAndreas Klinger 			mutex_lock(&data->lock);
2911081b9d9SAndreas Klinger 			data->rht = val;
2921081b9d9SAndreas Klinger 			mutex_unlock(&data->lock);
2931081b9d9SAndreas Klinger 			return 0;
2941081b9d9SAndreas Klinger 		default:
2951081b9d9SAndreas Klinger 			return -EINVAL;
2961081b9d9SAndreas Klinger 		}
2971081b9d9SAndreas Klinger 	case IIO_CHAN_INFO_CALIBBIAS:
2981081b9d9SAndreas Klinger 		if ((val < 20000) || (val > 52768))
2991081b9d9SAndreas Klinger 			return -EINVAL;
3001081b9d9SAndreas Klinger 
3011081b9d9SAndreas Klinger 		mutex_lock(&data->lock);
3021081b9d9SAndreas Klinger 		data->res_calibbias = val;
3031081b9d9SAndreas Klinger 		mutex_unlock(&data->lock);
3041081b9d9SAndreas Klinger 		return 0;
3051081b9d9SAndreas Klinger 	}
3061081b9d9SAndreas Klinger 	return -EINVAL;
3071081b9d9SAndreas Klinger }
3081081b9d9SAndreas Klinger 
3091081b9d9SAndreas Klinger static const struct iio_info sgp40_info = {
3101081b9d9SAndreas Klinger 	.read_raw	= sgp40_read_raw,
3111081b9d9SAndreas Klinger 	.write_raw	= sgp40_write_raw,
3121081b9d9SAndreas Klinger };
3131081b9d9SAndreas Klinger 
sgp40_probe(struct i2c_client * client)31407eda54dSUwe Kleine-König static int sgp40_probe(struct i2c_client *client)
3151081b9d9SAndreas Klinger {
31607eda54dSUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
3171081b9d9SAndreas Klinger 	struct device *dev = &client->dev;
3181081b9d9SAndreas Klinger 	struct iio_dev *indio_dev;
3191081b9d9SAndreas Klinger 	struct sgp40_data *data;
3201081b9d9SAndreas Klinger 	int ret;
3211081b9d9SAndreas Klinger 
3221081b9d9SAndreas Klinger 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
3231081b9d9SAndreas Klinger 	if (!indio_dev)
3241081b9d9SAndreas Klinger 		return -ENOMEM;
3251081b9d9SAndreas Klinger 
3261081b9d9SAndreas Klinger 	data = iio_priv(indio_dev);
3271081b9d9SAndreas Klinger 	data->client = client;
3281081b9d9SAndreas Klinger 	data->dev = dev;
3291081b9d9SAndreas Klinger 
3301081b9d9SAndreas Klinger 	crc8_populate_msb(sgp40_crc8_table, SGP40_CRC8_POLYNOMIAL);
3311081b9d9SAndreas Klinger 
3321081b9d9SAndreas Klinger 	mutex_init(&data->lock);
3331081b9d9SAndreas Klinger 
3341081b9d9SAndreas Klinger 	/* set default values */
3351081b9d9SAndreas Klinger 	data->rht = 50000;		/* 50 % */
3361081b9d9SAndreas Klinger 	data->temp = 25000;		/* 25 °C */
3371081b9d9SAndreas Klinger 	data->res_calibbias = 30000;	/* resistance raw value for voc index of 250 */
3381081b9d9SAndreas Klinger 
3391081b9d9SAndreas Klinger 	indio_dev->info = &sgp40_info;
3401081b9d9SAndreas Klinger 	indio_dev->name = id->name;
3411081b9d9SAndreas Klinger 	indio_dev->modes = INDIO_DIRECT_MODE;
3421081b9d9SAndreas Klinger 	indio_dev->channels = sgp40_channels;
3431081b9d9SAndreas Klinger 	indio_dev->num_channels = ARRAY_SIZE(sgp40_channels);
3441081b9d9SAndreas Klinger 
3451081b9d9SAndreas Klinger 	ret = devm_iio_device_register(dev, indio_dev);
3461081b9d9SAndreas Klinger 	if (ret)
3471081b9d9SAndreas Klinger 		dev_err(dev, "failed to register iio device\n");
3481081b9d9SAndreas Klinger 
3491081b9d9SAndreas Klinger 	return ret;
3501081b9d9SAndreas Klinger }
3511081b9d9SAndreas Klinger 
3521081b9d9SAndreas Klinger static const struct i2c_device_id sgp40_id[] = {
3531081b9d9SAndreas Klinger 	{ "sgp40" },
3541081b9d9SAndreas Klinger 	{ }
3551081b9d9SAndreas Klinger };
3561081b9d9SAndreas Klinger 
3571081b9d9SAndreas Klinger MODULE_DEVICE_TABLE(i2c, sgp40_id);
3581081b9d9SAndreas Klinger 
3591081b9d9SAndreas Klinger static const struct of_device_id sgp40_dt_ids[] = {
3601081b9d9SAndreas Klinger 	{ .compatible = "sensirion,sgp40" },
3611081b9d9SAndreas Klinger 	{ }
3621081b9d9SAndreas Klinger };
3631081b9d9SAndreas Klinger 
3641081b9d9SAndreas Klinger MODULE_DEVICE_TABLE(of, sgp40_dt_ids);
3651081b9d9SAndreas Klinger 
3661081b9d9SAndreas Klinger static struct i2c_driver sgp40_driver = {
3671081b9d9SAndreas Klinger 	.driver = {
3681081b9d9SAndreas Klinger 		.name = "sgp40",
3691081b9d9SAndreas Klinger 		.of_match_table = sgp40_dt_ids,
3701081b9d9SAndreas Klinger 	},
371*7cf15f42SUwe Kleine-König 	.probe = sgp40_probe,
3721081b9d9SAndreas Klinger 	.id_table = sgp40_id,
3731081b9d9SAndreas Klinger };
3741081b9d9SAndreas Klinger module_i2c_driver(sgp40_driver);
3751081b9d9SAndreas Klinger 
3761081b9d9SAndreas Klinger MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
3771081b9d9SAndreas Klinger MODULE_DESCRIPTION("Sensirion SGP40 gas sensor");
3781081b9d9SAndreas Klinger MODULE_LICENSE("GPL v2");
379