// SPDX-License-Identifier: GPL-2.0+ /* * sgp40.c - Support for Sensirion SGP40 Gas Sensor * * Copyright (C) 2021 Andreas Klinger <[email protected]> * * I2C slave address: 0x59 * * Datasheet can be found here: * https://www.sensirion.com/file/datasheet_sgp40 * * There are two functionalities supported: * * 1) read raw logarithmic resistance value from sensor * --> useful to pass it to the algorithm of the sensor vendor for * measuring deteriorations and improvements of air quality. * * 2) calculate an estimated absolute voc index (0 - 500 index points) for * measuring the air quality. * For this purpose the value of the resistance for which the voc index * will be 250 can be set up using calibbias. * * Compensation values of relative humidity and temperature can be set up * by writing to the out values of temp and humidityrelative. */ #include <linux/delay.h> #include <linux/crc8.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/i2c.h> #include <linux/iio/iio.h> /* * floating point calculation of voc is done as integer * where numbers are multiplied by 1 << SGP40_CALC_POWER */ #define SGP40_CALC_POWER … #define SGP40_CRC8_POLYNOMIAL … #define SGP40_CRC8_INIT … DECLARE_CRC8_TABLE(sgp40_crc8_table); struct sgp40_data { … }; struct sgp40_tg_measure { … } __packed; struct sgp40_tg_result { … } __packed; static const struct iio_chan_spec sgp40_channels[] = …; /* * taylor approximation of e^x: * y = 1 + x + x^2 / 2 + x^3 / 6 + x^4 / 24 + ... + x^n / n! * * Because we are calculating x real value multiplied by 2^power we get * an additional 2^power^n to divide for every element. For a reasonable * precision this would overflow after a few iterations. Therefore we * divide the x^n part whenever its about to overflow (xmax). */ static u32 sgp40_exp(int exp, u32 power, u32 rounds) { … } static int sgp40_calc_voc(struct sgp40_data *data, u16 resistance_raw, int *voc) { … } static int sgp40_measure_resistance_raw(struct sgp40_data *data, u16 *resistance_raw) { … } static int sgp40_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { … } static int sgp40_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { … } static const struct iio_info sgp40_info = …; static int sgp40_probe(struct i2c_client *client) { … } static const struct i2c_device_id sgp40_id[] = …; MODULE_DEVICE_TABLE(i2c, sgp40_id); static const struct of_device_id sgp40_dt_ids[] = …; MODULE_DEVICE_TABLE(of, sgp40_dt_ids); static struct i2c_driver sgp40_driver = …; module_i2c_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;