14c3dd9cdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2064a7463SDavid Barksdale /* 3064a7463SDavid Barksdale * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors 4064a7463SDavid Barksdale * Copyright (c) 2013,2014 Uplogix, Inc. 5064a7463SDavid Barksdale * David Barksdale <dbarksdale@uplogix.com> 6064a7463SDavid Barksdale */ 7064a7463SDavid Barksdale 8064a7463SDavid Barksdale /* 9064a7463SDavid Barksdale * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors 10064a7463SDavid Barksdale * are i2c devices which have an identical programming interface for 11064a7463SDavid Barksdale * measuring relative humidity and temperature. The Si7013 has an additional 12064a7463SDavid Barksdale * temperature input which this driver does not support. 13064a7463SDavid Barksdale * 14064a7463SDavid Barksdale * Data Sheets: 15064a7463SDavid Barksdale * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf 16064a7463SDavid Barksdale * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf 17064a7463SDavid Barksdale * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf 18064a7463SDavid Barksdale */ 19064a7463SDavid Barksdale 20064a7463SDavid Barksdale #include <linux/delay.h> 21064a7463SDavid Barksdale #include <linux/i2c.h> 22064a7463SDavid Barksdale #include <linux/module.h> 234231f9d1SJonathan Cameron #include <linux/mod_devicetable.h> 24064a7463SDavid Barksdale #include <linux/slab.h> 25064a7463SDavid Barksdale #include <linux/sysfs.h> 26064a7463SDavid Barksdale 27064a7463SDavid Barksdale #include <linux/iio/iio.h> 28064a7463SDavid Barksdale #include <linux/iio/sysfs.h> 29064a7463SDavid Barksdale 30064a7463SDavid Barksdale /* Measure Relative Humidity, Hold Master Mode */ 31064a7463SDavid Barksdale #define SI7020CMD_RH_HOLD 0xE5 32064a7463SDavid Barksdale /* Measure Temperature, Hold Master Mode */ 33064a7463SDavid Barksdale #define SI7020CMD_TEMP_HOLD 0xE3 34064a7463SDavid Barksdale /* Software Reset */ 35064a7463SDavid Barksdale #define SI7020CMD_RESET 0xFE 36064a7463SDavid Barksdale 37064a7463SDavid Barksdale static int si7020_read_raw(struct iio_dev *indio_dev, 38064a7463SDavid Barksdale struct iio_chan_spec const *chan, int *val, 39064a7463SDavid Barksdale int *val2, long mask) 40064a7463SDavid Barksdale { 41e765537aSJonathan Cameron struct i2c_client **client = iio_priv(indio_dev); 42064a7463SDavid Barksdale int ret; 43064a7463SDavid Barksdale 44064a7463SDavid Barksdale switch (mask) { 45064a7463SDavid Barksdale case IIO_CHAN_INFO_RAW: 460d2f6fd3SChris Lesiak ret = i2c_smbus_read_word_swapped(*client, 47064a7463SDavid Barksdale chan->type == IIO_TEMP ? 48064a7463SDavid Barksdale SI7020CMD_TEMP_HOLD : 49064a7463SDavid Barksdale SI7020CMD_RH_HOLD); 50064a7463SDavid Barksdale if (ret < 0) 51064a7463SDavid Barksdale return ret; 52064a7463SDavid Barksdale *val = ret >> 2; 53ecf7e207SNicola Corna /* 54ecf7e207SNicola Corna * Humidity values can slightly exceed the 0-100%RH 55ecf7e207SNicola Corna * range and should be corrected by software 56ecf7e207SNicola Corna */ 57345b4830SHartmut Knaack if (chan->type == IIO_HUMIDITYRELATIVE) 58ecf7e207SNicola Corna *val = clamp_val(*val, 786, 13893); 59064a7463SDavid Barksdale return IIO_VAL_INT; 60064a7463SDavid Barksdale case IIO_CHAN_INFO_SCALE: 61064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 62064a7463SDavid Barksdale *val = 175720; /* = 175.72 * 1000 */ 63064a7463SDavid Barksdale else 64064a7463SDavid Barksdale *val = 125 * 1000; 65064a7463SDavid Barksdale *val2 = 65536 >> 2; 66064a7463SDavid Barksdale return IIO_VAL_FRACTIONAL; 67064a7463SDavid Barksdale case IIO_CHAN_INFO_OFFSET: 68064a7463SDavid Barksdale /* 69064a7463SDavid Barksdale * Since iio_convert_raw_to_processed_unlocked assumes offset 70064a7463SDavid Barksdale * is an integer we have to round these values and lose 71064a7463SDavid Barksdale * accuracy. 72064a7463SDavid Barksdale * Relative humidity will be 0.0032959% too high and 73064a7463SDavid Barksdale * temperature will be 0.00277344 degrees too high. 74064a7463SDavid Barksdale * This is no big deal because it's within the accuracy of the 75064a7463SDavid Barksdale * sensor. 76064a7463SDavid Barksdale */ 77064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 78064a7463SDavid Barksdale *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 79064a7463SDavid Barksdale else 80064a7463SDavid Barksdale *val = -786; /* = -6 * (65536 >> 2) / 125 */ 81064a7463SDavid Barksdale return IIO_VAL_INT; 82064a7463SDavid Barksdale default: 83064a7463SDavid Barksdale break; 84064a7463SDavid Barksdale } 85064a7463SDavid Barksdale 86064a7463SDavid Barksdale return -EINVAL; 87064a7463SDavid Barksdale } 88064a7463SDavid Barksdale 89064a7463SDavid Barksdale static const struct iio_chan_spec si7020_channels[] = { 90064a7463SDavid Barksdale { 91064a7463SDavid Barksdale .type = IIO_HUMIDITYRELATIVE, 92064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 93064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 94064a7463SDavid Barksdale }, 95064a7463SDavid Barksdale { 96064a7463SDavid Barksdale .type = IIO_TEMP, 97064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 98064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 99064a7463SDavid Barksdale } 100064a7463SDavid Barksdale }; 101064a7463SDavid Barksdale 102064a7463SDavid Barksdale static const struct iio_info si7020_info = { 103064a7463SDavid Barksdale .read_raw = si7020_read_raw, 104064a7463SDavid Barksdale }; 105064a7463SDavid Barksdale 106*e6b61055SUwe Kleine-König static int si7020_probe(struct i2c_client *client) 107064a7463SDavid Barksdale { 108064a7463SDavid Barksdale struct iio_dev *indio_dev; 109064a7463SDavid Barksdale struct i2c_client **data; 110064a7463SDavid Barksdale int ret; 111064a7463SDavid Barksdale 112064a7463SDavid Barksdale if (!i2c_check_functionality(client->adapter, 113064a7463SDavid Barksdale I2C_FUNC_SMBUS_WRITE_BYTE | 114064a7463SDavid Barksdale I2C_FUNC_SMBUS_READ_WORD_DATA)) 115f8d9d3b4SMatt Ranostay return -EOPNOTSUPP; 116064a7463SDavid Barksdale 117064a7463SDavid Barksdale /* Reset device, loads default settings. */ 118064a7463SDavid Barksdale ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); 119064a7463SDavid Barksdale if (ret < 0) 120064a7463SDavid Barksdale return ret; 121064a7463SDavid Barksdale /* Wait the maximum power-up time after software reset. */ 122064a7463SDavid Barksdale msleep(15); 123064a7463SDavid Barksdale 124e01becbaSAndrey Smirnov indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 125064a7463SDavid Barksdale if (!indio_dev) 126064a7463SDavid Barksdale return -ENOMEM; 127064a7463SDavid Barksdale 128064a7463SDavid Barksdale data = iio_priv(indio_dev); 129064a7463SDavid Barksdale *data = client; 130064a7463SDavid Barksdale 131064a7463SDavid Barksdale indio_dev->name = dev_name(&client->dev); 132064a7463SDavid Barksdale indio_dev->modes = INDIO_DIRECT_MODE; 133064a7463SDavid Barksdale indio_dev->info = &si7020_info; 134064a7463SDavid Barksdale indio_dev->channels = si7020_channels; 135064a7463SDavid Barksdale indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 136064a7463SDavid Barksdale 137064a7463SDavid Barksdale return devm_iio_device_register(&client->dev, indio_dev); 138064a7463SDavid Barksdale } 139064a7463SDavid Barksdale 140064a7463SDavid Barksdale static const struct i2c_device_id si7020_id[] = { 141064a7463SDavid Barksdale { "si7020", 0 }, 142920dad0cSCristina Moraru { "th06", 0 }, 143064a7463SDavid Barksdale { } 144064a7463SDavid Barksdale }; 145064a7463SDavid Barksdale MODULE_DEVICE_TABLE(i2c, si7020_id); 146064a7463SDavid Barksdale 147b0d80198SPaul Kocialkowski static const struct of_device_id si7020_dt_ids[] = { 148b0d80198SPaul Kocialkowski { .compatible = "silabs,si7020" }, 149b0d80198SPaul Kocialkowski { } 150b0d80198SPaul Kocialkowski }; 151b0d80198SPaul Kocialkowski MODULE_DEVICE_TABLE(of, si7020_dt_ids); 152b0d80198SPaul Kocialkowski 153064a7463SDavid Barksdale static struct i2c_driver si7020_driver = { 154b0d80198SPaul Kocialkowski .driver = { 155b0d80198SPaul Kocialkowski .name = "si7020", 1564231f9d1SJonathan Cameron .of_match_table = si7020_dt_ids, 157b0d80198SPaul Kocialkowski }, 158*e6b61055SUwe Kleine-König .probe_new = si7020_probe, 159064a7463SDavid Barksdale .id_table = si7020_id, 160064a7463SDavid Barksdale }; 161064a7463SDavid Barksdale 162064a7463SDavid Barksdale module_i2c_driver(si7020_driver); 163064a7463SDavid Barksdale MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 164064a7463SDavid Barksdale MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 165064a7463SDavid Barksdale MODULE_LICENSE("GPL"); 166