1*1081b9d9SAndreas Klinger // SPDX-License-Identifier: GPL-2.0+ 2*1081b9d9SAndreas Klinger /* 3*1081b9d9SAndreas Klinger * sgp40.c - Support for Sensirion SGP40 Gas Sensor 4*1081b9d9SAndreas Klinger * 5*1081b9d9SAndreas Klinger * Copyright (C) 2021 Andreas Klinger <ak@it-klinger.de> 6*1081b9d9SAndreas Klinger * 7*1081b9d9SAndreas Klinger * I2C slave address: 0x59 8*1081b9d9SAndreas Klinger * 9*1081b9d9SAndreas Klinger * Datasheet can be found here: 10*1081b9d9SAndreas Klinger * https://www.sensirion.com/file/datasheet_sgp40 11*1081b9d9SAndreas Klinger * 12*1081b9d9SAndreas Klinger * There are two functionalities supported: 13*1081b9d9SAndreas Klinger * 14*1081b9d9SAndreas Klinger * 1) read raw logarithmic resistance value from sensor 15*1081b9d9SAndreas Klinger * --> useful to pass it to the algorithm of the sensor vendor for 16*1081b9d9SAndreas Klinger * measuring deteriorations and improvements of air quality. 17*1081b9d9SAndreas Klinger * 18*1081b9d9SAndreas Klinger * 2) calculate an estimated absolute voc index (0 - 500 index points) for 19*1081b9d9SAndreas Klinger * measuring the air quality. 20*1081b9d9SAndreas Klinger * For this purpose the value of the resistance for which the voc index 21*1081b9d9SAndreas Klinger * will be 250 can be set up using calibbias. 22*1081b9d9SAndreas Klinger * 23*1081b9d9SAndreas Klinger * Compensation values of relative humidity and temperature can be set up 24*1081b9d9SAndreas Klinger * by writing to the out values of temp and humidityrelative. 25*1081b9d9SAndreas Klinger */ 26*1081b9d9SAndreas Klinger 27*1081b9d9SAndreas Klinger #include <linux/delay.h> 28*1081b9d9SAndreas Klinger #include <linux/crc8.h> 29*1081b9d9SAndreas Klinger #include <linux/module.h> 30*1081b9d9SAndreas Klinger #include <linux/mutex.h> 31*1081b9d9SAndreas Klinger #include <linux/i2c.h> 32*1081b9d9SAndreas Klinger #include <linux/iio/iio.h> 33*1081b9d9SAndreas Klinger 34*1081b9d9SAndreas Klinger /* 35*1081b9d9SAndreas Klinger * floating point calculation of voc is done as integer 36*1081b9d9SAndreas Klinger * where numbers are multiplied by 1 << SGP40_CALC_POWER 37*1081b9d9SAndreas Klinger */ 38*1081b9d9SAndreas Klinger #define SGP40_CALC_POWER 14 39*1081b9d9SAndreas Klinger 40*1081b9d9SAndreas Klinger #define SGP40_CRC8_POLYNOMIAL 0x31 41*1081b9d9SAndreas Klinger #define SGP40_CRC8_INIT 0xff 42*1081b9d9SAndreas Klinger 43*1081b9d9SAndreas Klinger DECLARE_CRC8_TABLE(sgp40_crc8_table); 44*1081b9d9SAndreas Klinger 45*1081b9d9SAndreas Klinger struct sgp40_data { 46*1081b9d9SAndreas Klinger struct device *dev; 47*1081b9d9SAndreas Klinger struct i2c_client *client; 48*1081b9d9SAndreas Klinger int rht; 49*1081b9d9SAndreas Klinger int temp; 50*1081b9d9SAndreas Klinger int res_calibbias; 51*1081b9d9SAndreas Klinger /* Prevent concurrent access to rht, tmp, calibbias */ 52*1081b9d9SAndreas Klinger struct mutex lock; 53*1081b9d9SAndreas Klinger }; 54*1081b9d9SAndreas Klinger 55*1081b9d9SAndreas Klinger struct sgp40_tg_measure { 56*1081b9d9SAndreas Klinger u8 command[2]; 57*1081b9d9SAndreas Klinger __be16 rht_ticks; 58*1081b9d9SAndreas Klinger u8 rht_crc; 59*1081b9d9SAndreas Klinger __be16 temp_ticks; 60*1081b9d9SAndreas Klinger u8 temp_crc; 61*1081b9d9SAndreas Klinger } __packed; 62*1081b9d9SAndreas Klinger 63*1081b9d9SAndreas Klinger struct sgp40_tg_result { 64*1081b9d9SAndreas Klinger __be16 res_ticks; 65*1081b9d9SAndreas Klinger u8 res_crc; 66*1081b9d9SAndreas Klinger } __packed; 67*1081b9d9SAndreas Klinger 68*1081b9d9SAndreas Klinger static const struct iio_chan_spec sgp40_channels[] = { 69*1081b9d9SAndreas Klinger { 70*1081b9d9SAndreas Klinger .type = IIO_CONCENTRATION, 71*1081b9d9SAndreas Klinger .channel2 = IIO_MOD_VOC, 72*1081b9d9SAndreas Klinger .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 73*1081b9d9SAndreas Klinger }, 74*1081b9d9SAndreas Klinger { 75*1081b9d9SAndreas Klinger .type = IIO_RESISTANCE, 76*1081b9d9SAndreas Klinger .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 77*1081b9d9SAndreas Klinger BIT(IIO_CHAN_INFO_CALIBBIAS), 78*1081b9d9SAndreas Klinger }, 79*1081b9d9SAndreas Klinger { 80*1081b9d9SAndreas Klinger .type = IIO_TEMP, 81*1081b9d9SAndreas Klinger .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 82*1081b9d9SAndreas Klinger .output = 1, 83*1081b9d9SAndreas Klinger }, 84*1081b9d9SAndreas Klinger { 85*1081b9d9SAndreas Klinger .type = IIO_HUMIDITYRELATIVE, 86*1081b9d9SAndreas Klinger .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 87*1081b9d9SAndreas Klinger .output = 1, 88*1081b9d9SAndreas Klinger }, 89*1081b9d9SAndreas Klinger }; 90*1081b9d9SAndreas Klinger 91*1081b9d9SAndreas Klinger /* 92*1081b9d9SAndreas Klinger * taylor approximation of e^x: 93*1081b9d9SAndreas Klinger * y = 1 + x + x^2 / 2 + x^3 / 6 + x^4 / 24 + ... + x^n / n! 94*1081b9d9SAndreas Klinger * 95*1081b9d9SAndreas Klinger * Because we are calculating x real value multiplied by 2^power we get 96*1081b9d9SAndreas Klinger * an additional 2^power^n to divide for every element. For a reasonable 97*1081b9d9SAndreas Klinger * precision this would overflow after a few iterations. Therefore we 98*1081b9d9SAndreas Klinger * divide the x^n part whenever its about to overflow (xmax). 99*1081b9d9SAndreas Klinger */ 100*1081b9d9SAndreas Klinger 101*1081b9d9SAndreas Klinger static u32 sgp40_exp(int exp, u32 power, u32 rounds) 102*1081b9d9SAndreas Klinger { 103*1081b9d9SAndreas Klinger u32 x, y, xp; 104*1081b9d9SAndreas Klinger u32 factorial, divider, xmax; 105*1081b9d9SAndreas Klinger int sign = 1; 106*1081b9d9SAndreas Klinger int i; 107*1081b9d9SAndreas Klinger 108*1081b9d9SAndreas Klinger if (exp == 0) 109*1081b9d9SAndreas Klinger return 1 << power; 110*1081b9d9SAndreas Klinger else if (exp < 0) { 111*1081b9d9SAndreas Klinger sign = -1; 112*1081b9d9SAndreas Klinger exp *= -1; 113*1081b9d9SAndreas Klinger } 114*1081b9d9SAndreas Klinger 115*1081b9d9SAndreas Klinger xmax = 0x7FFFFFFF / exp; 116*1081b9d9SAndreas Klinger x = exp; 117*1081b9d9SAndreas Klinger xp = 1; 118*1081b9d9SAndreas Klinger factorial = 1; 119*1081b9d9SAndreas Klinger y = 1 << power; 120*1081b9d9SAndreas Klinger divider = 0; 121*1081b9d9SAndreas Klinger 122*1081b9d9SAndreas Klinger for (i = 1; i <= rounds; i++) { 123*1081b9d9SAndreas Klinger xp *= x; 124*1081b9d9SAndreas Klinger factorial *= i; 125*1081b9d9SAndreas Klinger y += (xp >> divider) / factorial; 126*1081b9d9SAndreas Klinger divider += power; 127*1081b9d9SAndreas Klinger /* divide when next multiplication would overflow */ 128*1081b9d9SAndreas Klinger if (xp >= xmax) { 129*1081b9d9SAndreas Klinger xp >>= power; 130*1081b9d9SAndreas Klinger divider -= power; 131*1081b9d9SAndreas Klinger } 132*1081b9d9SAndreas Klinger } 133*1081b9d9SAndreas Klinger 134*1081b9d9SAndreas Klinger if (sign == -1) 135*1081b9d9SAndreas Klinger return (1 << (power * 2)) / y; 136*1081b9d9SAndreas Klinger else 137*1081b9d9SAndreas Klinger return y; 138*1081b9d9SAndreas Klinger } 139*1081b9d9SAndreas Klinger 140*1081b9d9SAndreas Klinger static int sgp40_calc_voc(struct sgp40_data *data, u16 resistance_raw, int *voc) 141*1081b9d9SAndreas Klinger { 142*1081b9d9SAndreas Klinger int x; 143*1081b9d9SAndreas Klinger u32 exp = 0; 144*1081b9d9SAndreas Klinger 145*1081b9d9SAndreas Klinger /* we calculate as a multiple of 16384 (2^14) */ 146*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 147*1081b9d9SAndreas Klinger x = ((int)resistance_raw - data->res_calibbias) * 106; 148*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 149*1081b9d9SAndreas Klinger 150*1081b9d9SAndreas Klinger /* voc = 500 / (1 + e^x) */ 151*1081b9d9SAndreas Klinger exp = sgp40_exp(x, SGP40_CALC_POWER, 18); 152*1081b9d9SAndreas Klinger *voc = 500 * ((1 << (SGP40_CALC_POWER * 2)) / ((1<<SGP40_CALC_POWER) + exp)); 153*1081b9d9SAndreas Klinger 154*1081b9d9SAndreas Klinger dev_dbg(data->dev, "raw: %d res_calibbias: %d x: %d exp: %d voc: %d\n", 155*1081b9d9SAndreas Klinger resistance_raw, data->res_calibbias, x, exp, *voc); 156*1081b9d9SAndreas Klinger 157*1081b9d9SAndreas Klinger return 0; 158*1081b9d9SAndreas Klinger } 159*1081b9d9SAndreas Klinger 160*1081b9d9SAndreas Klinger static int sgp40_measure_resistance_raw(struct sgp40_data *data, u16 *resistance_raw) 161*1081b9d9SAndreas Klinger { 162*1081b9d9SAndreas Klinger int ret; 163*1081b9d9SAndreas Klinger struct i2c_client *client = data->client; 164*1081b9d9SAndreas Klinger u32 ticks; 165*1081b9d9SAndreas Klinger u16 ticks16; 166*1081b9d9SAndreas Klinger u8 crc; 167*1081b9d9SAndreas Klinger struct sgp40_tg_measure tg = {.command = {0x26, 0x0F}}; 168*1081b9d9SAndreas Klinger struct sgp40_tg_result tgres; 169*1081b9d9SAndreas Klinger 170*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 171*1081b9d9SAndreas Klinger 172*1081b9d9SAndreas Klinger ticks = (data->rht / 10) * 65535 / 10000; 173*1081b9d9SAndreas Klinger ticks16 = (u16)clamp(ticks, 0u, 65535u); /* clamp between 0 .. 100 %rH */ 174*1081b9d9SAndreas Klinger tg.rht_ticks = cpu_to_be16(ticks16); 175*1081b9d9SAndreas Klinger tg.rht_crc = crc8(sgp40_crc8_table, (u8 *)&tg.rht_ticks, 2, SGP40_CRC8_INIT); 176*1081b9d9SAndreas Klinger 177*1081b9d9SAndreas Klinger ticks = ((data->temp + 45000) / 10 ) * 65535 / 17500; 178*1081b9d9SAndreas Klinger ticks16 = (u16)clamp(ticks, 0u, 65535u); /* clamp between -45 .. +130 °C */ 179*1081b9d9SAndreas Klinger tg.temp_ticks = cpu_to_be16(ticks16); 180*1081b9d9SAndreas Klinger tg.temp_crc = crc8(sgp40_crc8_table, (u8 *)&tg.temp_ticks, 2, SGP40_CRC8_INIT); 181*1081b9d9SAndreas Klinger 182*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 183*1081b9d9SAndreas Klinger 184*1081b9d9SAndreas Klinger ret = i2c_master_send(client, (const char *)&tg, sizeof(tg)); 185*1081b9d9SAndreas Klinger if (ret != sizeof(tg)) { 186*1081b9d9SAndreas Klinger dev_warn(data->dev, "i2c_master_send ret: %d sizeof: %zu\n", ret, sizeof(tg)); 187*1081b9d9SAndreas Klinger return -EIO; 188*1081b9d9SAndreas Klinger } 189*1081b9d9SAndreas Klinger msleep(30); 190*1081b9d9SAndreas Klinger 191*1081b9d9SAndreas Klinger ret = i2c_master_recv(client, (u8 *)&tgres, sizeof(tgres)); 192*1081b9d9SAndreas Klinger if (ret < 0) 193*1081b9d9SAndreas Klinger return ret; 194*1081b9d9SAndreas Klinger if (ret != sizeof(tgres)) { 195*1081b9d9SAndreas Klinger dev_warn(data->dev, "i2c_master_recv ret: %d sizeof: %zu\n", ret, sizeof(tgres)); 196*1081b9d9SAndreas Klinger return -EIO; 197*1081b9d9SAndreas Klinger } 198*1081b9d9SAndreas Klinger 199*1081b9d9SAndreas Klinger crc = crc8(sgp40_crc8_table, (u8 *)&tgres.res_ticks, 2, SGP40_CRC8_INIT); 200*1081b9d9SAndreas Klinger if (crc != tgres.res_crc) { 201*1081b9d9SAndreas Klinger dev_err(data->dev, "CRC error while measure-raw\n"); 202*1081b9d9SAndreas Klinger return -EIO; 203*1081b9d9SAndreas Klinger } 204*1081b9d9SAndreas Klinger 205*1081b9d9SAndreas Klinger *resistance_raw = be16_to_cpu(tgres.res_ticks); 206*1081b9d9SAndreas Klinger 207*1081b9d9SAndreas Klinger return 0; 208*1081b9d9SAndreas Klinger } 209*1081b9d9SAndreas Klinger 210*1081b9d9SAndreas Klinger static int sgp40_read_raw(struct iio_dev *indio_dev, 211*1081b9d9SAndreas Klinger struct iio_chan_spec const *chan, int *val, 212*1081b9d9SAndreas Klinger int *val2, long mask) 213*1081b9d9SAndreas Klinger { 214*1081b9d9SAndreas Klinger struct sgp40_data *data = iio_priv(indio_dev); 215*1081b9d9SAndreas Klinger int ret, voc; 216*1081b9d9SAndreas Klinger u16 resistance_raw; 217*1081b9d9SAndreas Klinger 218*1081b9d9SAndreas Klinger switch (mask) { 219*1081b9d9SAndreas Klinger case IIO_CHAN_INFO_RAW: 220*1081b9d9SAndreas Klinger switch (chan->type) { 221*1081b9d9SAndreas Klinger case IIO_RESISTANCE: 222*1081b9d9SAndreas Klinger ret = sgp40_measure_resistance_raw(data, &resistance_raw); 223*1081b9d9SAndreas Klinger if (ret) 224*1081b9d9SAndreas Klinger return ret; 225*1081b9d9SAndreas Klinger 226*1081b9d9SAndreas Klinger *val = resistance_raw; 227*1081b9d9SAndreas Klinger return IIO_VAL_INT; 228*1081b9d9SAndreas Klinger case IIO_TEMP: 229*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 230*1081b9d9SAndreas Klinger *val = data->temp; 231*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 232*1081b9d9SAndreas Klinger return IIO_VAL_INT; 233*1081b9d9SAndreas Klinger case IIO_HUMIDITYRELATIVE: 234*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 235*1081b9d9SAndreas Klinger *val = data->rht; 236*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 237*1081b9d9SAndreas Klinger return IIO_VAL_INT; 238*1081b9d9SAndreas Klinger default: 239*1081b9d9SAndreas Klinger return -EINVAL; 240*1081b9d9SAndreas Klinger } 241*1081b9d9SAndreas Klinger case IIO_CHAN_INFO_PROCESSED: 242*1081b9d9SAndreas Klinger ret = sgp40_measure_resistance_raw(data, &resistance_raw); 243*1081b9d9SAndreas Klinger if (ret) 244*1081b9d9SAndreas Klinger return ret; 245*1081b9d9SAndreas Klinger 246*1081b9d9SAndreas Klinger ret = sgp40_calc_voc(data, resistance_raw, &voc); 247*1081b9d9SAndreas Klinger if (ret) 248*1081b9d9SAndreas Klinger return ret; 249*1081b9d9SAndreas Klinger 250*1081b9d9SAndreas Klinger *val = voc / (1 << SGP40_CALC_POWER); 251*1081b9d9SAndreas Klinger /* 252*1081b9d9SAndreas Klinger * calculation should fit into integer, where: 253*1081b9d9SAndreas Klinger * voc <= (500 * 2^SGP40_CALC_POWER) = 8192000 254*1081b9d9SAndreas Klinger * (with SGP40_CALC_POWER = 14) 255*1081b9d9SAndreas Klinger */ 256*1081b9d9SAndreas Klinger *val2 = ((voc % (1 << SGP40_CALC_POWER)) * 244) / (1 << (SGP40_CALC_POWER - 12)); 257*1081b9d9SAndreas Klinger dev_dbg(data->dev, "voc: %d val: %d.%06d\n", voc, *val, *val2); 258*1081b9d9SAndreas Klinger return IIO_VAL_INT_PLUS_MICRO; 259*1081b9d9SAndreas Klinger case IIO_CHAN_INFO_CALIBBIAS: 260*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 261*1081b9d9SAndreas Klinger *val = data->res_calibbias; 262*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 263*1081b9d9SAndreas Klinger return IIO_VAL_INT; 264*1081b9d9SAndreas Klinger default: 265*1081b9d9SAndreas Klinger return -EINVAL; 266*1081b9d9SAndreas Klinger } 267*1081b9d9SAndreas Klinger } 268*1081b9d9SAndreas Klinger 269*1081b9d9SAndreas Klinger static int sgp40_write_raw(struct iio_dev *indio_dev, 270*1081b9d9SAndreas Klinger struct iio_chan_spec const *chan, int val, 271*1081b9d9SAndreas Klinger int val2, long mask) 272*1081b9d9SAndreas Klinger { 273*1081b9d9SAndreas Klinger struct sgp40_data *data = iio_priv(indio_dev); 274*1081b9d9SAndreas Klinger 275*1081b9d9SAndreas Klinger switch (mask) { 276*1081b9d9SAndreas Klinger case IIO_CHAN_INFO_RAW: 277*1081b9d9SAndreas Klinger switch (chan->type) { 278*1081b9d9SAndreas Klinger case IIO_TEMP: 279*1081b9d9SAndreas Klinger if ((val < -45000) || (val > 130000)) 280*1081b9d9SAndreas Klinger return -EINVAL; 281*1081b9d9SAndreas Klinger 282*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 283*1081b9d9SAndreas Klinger data->temp = val; 284*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 285*1081b9d9SAndreas Klinger return 0; 286*1081b9d9SAndreas Klinger case IIO_HUMIDITYRELATIVE: 287*1081b9d9SAndreas Klinger if ((val < 0) || (val > 100000)) 288*1081b9d9SAndreas Klinger return -EINVAL; 289*1081b9d9SAndreas Klinger 290*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 291*1081b9d9SAndreas Klinger data->rht = val; 292*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 293*1081b9d9SAndreas Klinger return 0; 294*1081b9d9SAndreas Klinger default: 295*1081b9d9SAndreas Klinger return -EINVAL; 296*1081b9d9SAndreas Klinger } 297*1081b9d9SAndreas Klinger case IIO_CHAN_INFO_CALIBBIAS: 298*1081b9d9SAndreas Klinger if ((val < 20000) || (val > 52768)) 299*1081b9d9SAndreas Klinger return -EINVAL; 300*1081b9d9SAndreas Klinger 301*1081b9d9SAndreas Klinger mutex_lock(&data->lock); 302*1081b9d9SAndreas Klinger data->res_calibbias = val; 303*1081b9d9SAndreas Klinger mutex_unlock(&data->lock); 304*1081b9d9SAndreas Klinger return 0; 305*1081b9d9SAndreas Klinger } 306*1081b9d9SAndreas Klinger return -EINVAL; 307*1081b9d9SAndreas Klinger } 308*1081b9d9SAndreas Klinger 309*1081b9d9SAndreas Klinger static const struct iio_info sgp40_info = { 310*1081b9d9SAndreas Klinger .read_raw = sgp40_read_raw, 311*1081b9d9SAndreas Klinger .write_raw = sgp40_write_raw, 312*1081b9d9SAndreas Klinger }; 313*1081b9d9SAndreas Klinger 314*1081b9d9SAndreas Klinger static int sgp40_probe(struct i2c_client *client, 315*1081b9d9SAndreas Klinger const struct i2c_device_id *id) 316*1081b9d9SAndreas Klinger { 317*1081b9d9SAndreas Klinger struct device *dev = &client->dev; 318*1081b9d9SAndreas Klinger struct iio_dev *indio_dev; 319*1081b9d9SAndreas Klinger struct sgp40_data *data; 320*1081b9d9SAndreas Klinger int ret; 321*1081b9d9SAndreas Klinger 322*1081b9d9SAndreas Klinger indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 323*1081b9d9SAndreas Klinger if (!indio_dev) 324*1081b9d9SAndreas Klinger return -ENOMEM; 325*1081b9d9SAndreas Klinger 326*1081b9d9SAndreas Klinger data = iio_priv(indio_dev); 327*1081b9d9SAndreas Klinger data->client = client; 328*1081b9d9SAndreas Klinger data->dev = dev; 329*1081b9d9SAndreas Klinger 330*1081b9d9SAndreas Klinger crc8_populate_msb(sgp40_crc8_table, SGP40_CRC8_POLYNOMIAL); 331*1081b9d9SAndreas Klinger 332*1081b9d9SAndreas Klinger mutex_init(&data->lock); 333*1081b9d9SAndreas Klinger 334*1081b9d9SAndreas Klinger /* set default values */ 335*1081b9d9SAndreas Klinger data->rht = 50000; /* 50 % */ 336*1081b9d9SAndreas Klinger data->temp = 25000; /* 25 °C */ 337*1081b9d9SAndreas Klinger data->res_calibbias = 30000; /* resistance raw value for voc index of 250 */ 338*1081b9d9SAndreas Klinger 339*1081b9d9SAndreas Klinger indio_dev->info = &sgp40_info; 340*1081b9d9SAndreas Klinger indio_dev->name = id->name; 341*1081b9d9SAndreas Klinger indio_dev->modes = INDIO_DIRECT_MODE; 342*1081b9d9SAndreas Klinger indio_dev->channels = sgp40_channels; 343*1081b9d9SAndreas Klinger indio_dev->num_channels = ARRAY_SIZE(sgp40_channels); 344*1081b9d9SAndreas Klinger 345*1081b9d9SAndreas Klinger ret = devm_iio_device_register(dev, indio_dev); 346*1081b9d9SAndreas Klinger if (ret) 347*1081b9d9SAndreas Klinger dev_err(dev, "failed to register iio device\n"); 348*1081b9d9SAndreas Klinger 349*1081b9d9SAndreas Klinger return ret; 350*1081b9d9SAndreas Klinger } 351*1081b9d9SAndreas Klinger 352*1081b9d9SAndreas Klinger static const struct i2c_device_id sgp40_id[] = { 353*1081b9d9SAndreas Klinger { "sgp40" }, 354*1081b9d9SAndreas Klinger { } 355*1081b9d9SAndreas Klinger }; 356*1081b9d9SAndreas Klinger 357*1081b9d9SAndreas Klinger MODULE_DEVICE_TABLE(i2c, sgp40_id); 358*1081b9d9SAndreas Klinger 359*1081b9d9SAndreas Klinger static const struct of_device_id sgp40_dt_ids[] = { 360*1081b9d9SAndreas Klinger { .compatible = "sensirion,sgp40" }, 361*1081b9d9SAndreas Klinger { } 362*1081b9d9SAndreas Klinger }; 363*1081b9d9SAndreas Klinger 364*1081b9d9SAndreas Klinger MODULE_DEVICE_TABLE(of, sgp40_dt_ids); 365*1081b9d9SAndreas Klinger 366*1081b9d9SAndreas Klinger static struct i2c_driver sgp40_driver = { 367*1081b9d9SAndreas Klinger .driver = { 368*1081b9d9SAndreas Klinger .name = "sgp40", 369*1081b9d9SAndreas Klinger .of_match_table = sgp40_dt_ids, 370*1081b9d9SAndreas Klinger }, 371*1081b9d9SAndreas Klinger .probe = sgp40_probe, 372*1081b9d9SAndreas Klinger .id_table = sgp40_id, 373*1081b9d9SAndreas Klinger }; 374*1081b9d9SAndreas Klinger module_i2c_driver(sgp40_driver); 375*1081b9d9SAndreas Klinger 376*1081b9d9SAndreas Klinger MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); 377*1081b9d9SAndreas Klinger MODULE_DESCRIPTION("Sensirion SGP40 gas sensor"); 378*1081b9d9SAndreas Klinger MODULE_LICENSE("GPL v2"); 379