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