1064a7463SDavid Barksdale /* 2064a7463SDavid Barksdale * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors 3064a7463SDavid Barksdale * Copyright (c) 2013,2014 Uplogix, Inc. 4064a7463SDavid Barksdale * David Barksdale <dbarksdale@uplogix.com> 5064a7463SDavid Barksdale * 6064a7463SDavid Barksdale * This program is free software; you can redistribute it and/or modify it 7064a7463SDavid Barksdale * under the terms and conditions of the GNU General Public License, 8064a7463SDavid Barksdale * version 2, as published by the Free Software Foundation. 9064a7463SDavid Barksdale * 10064a7463SDavid Barksdale * This program is distributed in the hope that it will be useful, 11064a7463SDavid Barksdale * but WITHOUT ANY WARRANTY; without even the implied warranty of 12064a7463SDavid Barksdale * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13064a7463SDavid Barksdale * GNU General Public License for more details. 14064a7463SDavid Barksdale */ 15064a7463SDavid Barksdale 16064a7463SDavid Barksdale /* 17064a7463SDavid Barksdale * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors 18064a7463SDavid Barksdale * are i2c devices which have an identical programming interface for 19064a7463SDavid Barksdale * measuring relative humidity and temperature. The Si7013 has an additional 20064a7463SDavid Barksdale * temperature input which this driver does not support. 21064a7463SDavid Barksdale * 22064a7463SDavid Barksdale * Data Sheets: 23064a7463SDavid Barksdale * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf 24064a7463SDavid Barksdale * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf 25064a7463SDavid Barksdale * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf 26064a7463SDavid Barksdale */ 27064a7463SDavid Barksdale 28064a7463SDavid Barksdale #include <linux/delay.h> 29064a7463SDavid Barksdale #include <linux/i2c.h> 30064a7463SDavid Barksdale #include <linux/module.h> 31064a7463SDavid Barksdale #include <linux/slab.h> 32064a7463SDavid Barksdale #include <linux/sysfs.h> 33064a7463SDavid Barksdale 34064a7463SDavid Barksdale #include <linux/iio/iio.h> 35064a7463SDavid Barksdale #include <linux/iio/sysfs.h> 36064a7463SDavid Barksdale 37064a7463SDavid Barksdale /* Measure Relative Humidity, Hold Master Mode */ 38064a7463SDavid Barksdale #define SI7020CMD_RH_HOLD 0xE5 39064a7463SDavid Barksdale /* Measure Temperature, Hold Master Mode */ 40064a7463SDavid Barksdale #define SI7020CMD_TEMP_HOLD 0xE3 41064a7463SDavid Barksdale /* Software Reset */ 42064a7463SDavid Barksdale #define SI7020CMD_RESET 0xFE 43064a7463SDavid Barksdale 44064a7463SDavid Barksdale static int si7020_read_raw(struct iio_dev *indio_dev, 45064a7463SDavid Barksdale struct iio_chan_spec const *chan, int *val, 46064a7463SDavid Barksdale int *val2, long mask) 47064a7463SDavid Barksdale { 48e765537aSJonathan Cameron struct i2c_client **client = iio_priv(indio_dev); 49064a7463SDavid Barksdale int ret; 50064a7463SDavid Barksdale 51064a7463SDavid Barksdale switch (mask) { 52064a7463SDavid Barksdale case IIO_CHAN_INFO_RAW: 530d2f6fd3SChris Lesiak ret = i2c_smbus_read_word_swapped(*client, 54064a7463SDavid Barksdale chan->type == IIO_TEMP ? 55064a7463SDavid Barksdale SI7020CMD_TEMP_HOLD : 56064a7463SDavid Barksdale SI7020CMD_RH_HOLD); 57064a7463SDavid Barksdale if (ret < 0) 58064a7463SDavid Barksdale return ret; 59064a7463SDavid Barksdale *val = ret >> 2; 60ecf7e207SNicola Corna /* 61ecf7e207SNicola Corna * Humidity values can slightly exceed the 0-100%RH 62ecf7e207SNicola Corna * range and should be corrected by software 63ecf7e207SNicola Corna */ 64345b4830SHartmut Knaack if (chan->type == IIO_HUMIDITYRELATIVE) 65ecf7e207SNicola Corna *val = clamp_val(*val, 786, 13893); 66064a7463SDavid Barksdale return IIO_VAL_INT; 67064a7463SDavid Barksdale case IIO_CHAN_INFO_SCALE: 68064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 69064a7463SDavid Barksdale *val = 175720; /* = 175.72 * 1000 */ 70064a7463SDavid Barksdale else 71064a7463SDavid Barksdale *val = 125 * 1000; 72064a7463SDavid Barksdale *val2 = 65536 >> 2; 73064a7463SDavid Barksdale return IIO_VAL_FRACTIONAL; 74064a7463SDavid Barksdale case IIO_CHAN_INFO_OFFSET: 75064a7463SDavid Barksdale /* 76064a7463SDavid Barksdale * Since iio_convert_raw_to_processed_unlocked assumes offset 77064a7463SDavid Barksdale * is an integer we have to round these values and lose 78064a7463SDavid Barksdale * accuracy. 79064a7463SDavid Barksdale * Relative humidity will be 0.0032959% too high and 80064a7463SDavid Barksdale * temperature will be 0.00277344 degrees too high. 81064a7463SDavid Barksdale * This is no big deal because it's within the accuracy of the 82064a7463SDavid Barksdale * sensor. 83064a7463SDavid Barksdale */ 84064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 85064a7463SDavid Barksdale *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 86064a7463SDavid Barksdale else 87064a7463SDavid Barksdale *val = -786; /* = -6 * (65536 >> 2) / 125 */ 88064a7463SDavid Barksdale return IIO_VAL_INT; 89064a7463SDavid Barksdale default: 90064a7463SDavid Barksdale break; 91064a7463SDavid Barksdale } 92064a7463SDavid Barksdale 93064a7463SDavid Barksdale return -EINVAL; 94064a7463SDavid Barksdale } 95064a7463SDavid Barksdale 96064a7463SDavid Barksdale static const struct iio_chan_spec si7020_channels[] = { 97064a7463SDavid Barksdale { 98064a7463SDavid Barksdale .type = IIO_HUMIDITYRELATIVE, 99064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 100064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 101064a7463SDavid Barksdale }, 102064a7463SDavid Barksdale { 103064a7463SDavid Barksdale .type = IIO_TEMP, 104064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 105064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 106064a7463SDavid Barksdale } 107064a7463SDavid Barksdale }; 108064a7463SDavid Barksdale 109064a7463SDavid Barksdale static const struct iio_info si7020_info = { 110064a7463SDavid Barksdale .read_raw = si7020_read_raw, 111064a7463SDavid Barksdale .driver_module = THIS_MODULE, 112064a7463SDavid Barksdale }; 113064a7463SDavid Barksdale 114064a7463SDavid Barksdale static int si7020_probe(struct i2c_client *client, 115064a7463SDavid Barksdale const struct i2c_device_id *id) 116064a7463SDavid Barksdale { 117064a7463SDavid Barksdale struct iio_dev *indio_dev; 118064a7463SDavid Barksdale struct i2c_client **data; 119064a7463SDavid Barksdale int ret; 120064a7463SDavid Barksdale 121064a7463SDavid Barksdale if (!i2c_check_functionality(client->adapter, 122064a7463SDavid Barksdale I2C_FUNC_SMBUS_WRITE_BYTE | 123064a7463SDavid Barksdale I2C_FUNC_SMBUS_READ_WORD_DATA)) 124064a7463SDavid Barksdale return -ENODEV; 125064a7463SDavid Barksdale 126064a7463SDavid Barksdale /* Reset device, loads default settings. */ 127064a7463SDavid Barksdale ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); 128064a7463SDavid Barksdale if (ret < 0) 129064a7463SDavid Barksdale return ret; 130064a7463SDavid Barksdale /* Wait the maximum power-up time after software reset. */ 131064a7463SDavid Barksdale msleep(15); 132064a7463SDavid Barksdale 133e01becbaSAndrey Smirnov indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 134064a7463SDavid Barksdale if (!indio_dev) 135064a7463SDavid Barksdale return -ENOMEM; 136064a7463SDavid Barksdale 137064a7463SDavid Barksdale data = iio_priv(indio_dev); 138064a7463SDavid Barksdale *data = client; 139064a7463SDavid Barksdale 140064a7463SDavid Barksdale indio_dev->dev.parent = &client->dev; 141064a7463SDavid Barksdale indio_dev->name = dev_name(&client->dev); 142064a7463SDavid Barksdale indio_dev->modes = INDIO_DIRECT_MODE; 143064a7463SDavid Barksdale indio_dev->info = &si7020_info; 144064a7463SDavid Barksdale indio_dev->channels = si7020_channels; 145064a7463SDavid Barksdale indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 146064a7463SDavid Barksdale 147064a7463SDavid Barksdale return devm_iio_device_register(&client->dev, indio_dev); 148064a7463SDavid Barksdale } 149064a7463SDavid Barksdale 150064a7463SDavid Barksdale static const struct i2c_device_id si7020_id[] = { 151064a7463SDavid Barksdale { "si7020", 0 }, 152*920dad0cSCristina Moraru { "th06", 0 }, 153064a7463SDavid Barksdale { } 154064a7463SDavid Barksdale }; 155064a7463SDavid Barksdale MODULE_DEVICE_TABLE(i2c, si7020_id); 156064a7463SDavid Barksdale 157064a7463SDavid Barksdale static struct i2c_driver si7020_driver = { 158064a7463SDavid Barksdale .driver.name = "si7020", 159064a7463SDavid Barksdale .probe = si7020_probe, 160064a7463SDavid Barksdale .id_table = si7020_id, 161064a7463SDavid Barksdale }; 162064a7463SDavid Barksdale 163064a7463SDavid Barksdale module_i2c_driver(si7020_driver); 164064a7463SDavid Barksdale MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 165064a7463SDavid Barksdale MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 166064a7463SDavid Barksdale MODULE_LICENSE("GPL"); 167