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 { 48*e765537aSJonathan 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: 53*e765537aSJonathan Cameron ret = i2c_smbus_read_word_data(*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; 60345b4830SHartmut Knaack if (chan->type == IIO_HUMIDITYRELATIVE) 61345b4830SHartmut Knaack *val &= GENMASK(11, 0); 62064a7463SDavid Barksdale return IIO_VAL_INT; 63064a7463SDavid Barksdale case IIO_CHAN_INFO_SCALE: 64064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 65064a7463SDavid Barksdale *val = 175720; /* = 175.72 * 1000 */ 66064a7463SDavid Barksdale else 67064a7463SDavid Barksdale *val = 125 * 1000; 68064a7463SDavid Barksdale *val2 = 65536 >> 2; 69064a7463SDavid Barksdale return IIO_VAL_FRACTIONAL; 70064a7463SDavid Barksdale case IIO_CHAN_INFO_OFFSET: 71064a7463SDavid Barksdale /* 72064a7463SDavid Barksdale * Since iio_convert_raw_to_processed_unlocked assumes offset 73064a7463SDavid Barksdale * is an integer we have to round these values and lose 74064a7463SDavid Barksdale * accuracy. 75064a7463SDavid Barksdale * Relative humidity will be 0.0032959% too high and 76064a7463SDavid Barksdale * temperature will be 0.00277344 degrees too high. 77064a7463SDavid Barksdale * This is no big deal because it's within the accuracy of the 78064a7463SDavid Barksdale * sensor. 79064a7463SDavid Barksdale */ 80064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 81064a7463SDavid Barksdale *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 82064a7463SDavid Barksdale else 83064a7463SDavid Barksdale *val = -786; /* = -6 * (65536 >> 2) / 125 */ 84064a7463SDavid Barksdale return IIO_VAL_INT; 85064a7463SDavid Barksdale default: 86064a7463SDavid Barksdale break; 87064a7463SDavid Barksdale } 88064a7463SDavid Barksdale 89064a7463SDavid Barksdale return -EINVAL; 90064a7463SDavid Barksdale } 91064a7463SDavid Barksdale 92064a7463SDavid Barksdale static const struct iio_chan_spec si7020_channels[] = { 93064a7463SDavid Barksdale { 94064a7463SDavid Barksdale .type = IIO_HUMIDITYRELATIVE, 95064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 96064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 97064a7463SDavid Barksdale }, 98064a7463SDavid Barksdale { 99064a7463SDavid Barksdale .type = IIO_TEMP, 100064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 101064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 102064a7463SDavid Barksdale } 103064a7463SDavid Barksdale }; 104064a7463SDavid Barksdale 105064a7463SDavid Barksdale static const struct iio_info si7020_info = { 106064a7463SDavid Barksdale .read_raw = si7020_read_raw, 107064a7463SDavid Barksdale .driver_module = THIS_MODULE, 108064a7463SDavid Barksdale }; 109064a7463SDavid Barksdale 110064a7463SDavid Barksdale static int si7020_probe(struct i2c_client *client, 111064a7463SDavid Barksdale const struct i2c_device_id *id) 112064a7463SDavid Barksdale { 113064a7463SDavid Barksdale struct iio_dev *indio_dev; 114064a7463SDavid Barksdale struct i2c_client **data; 115064a7463SDavid Barksdale int ret; 116064a7463SDavid Barksdale 117064a7463SDavid Barksdale if (!i2c_check_functionality(client->adapter, 118064a7463SDavid Barksdale I2C_FUNC_SMBUS_WRITE_BYTE | 119064a7463SDavid Barksdale I2C_FUNC_SMBUS_READ_WORD_DATA)) 120064a7463SDavid Barksdale return -ENODEV; 121064a7463SDavid Barksdale 122064a7463SDavid Barksdale /* Reset device, loads default settings. */ 123064a7463SDavid Barksdale ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); 124064a7463SDavid Barksdale if (ret < 0) 125064a7463SDavid Barksdale return ret; 126064a7463SDavid Barksdale /* Wait the maximum power-up time after software reset. */ 127064a7463SDavid Barksdale msleep(15); 128064a7463SDavid Barksdale 129064a7463SDavid Barksdale indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client)); 130064a7463SDavid Barksdale if (!indio_dev) 131064a7463SDavid Barksdale return -ENOMEM; 132064a7463SDavid Barksdale 133064a7463SDavid Barksdale data = iio_priv(indio_dev); 134064a7463SDavid Barksdale *data = client; 135064a7463SDavid Barksdale 136064a7463SDavid Barksdale indio_dev->dev.parent = &client->dev; 137064a7463SDavid Barksdale indio_dev->name = dev_name(&client->dev); 138064a7463SDavid Barksdale indio_dev->modes = INDIO_DIRECT_MODE; 139064a7463SDavid Barksdale indio_dev->info = &si7020_info; 140064a7463SDavid Barksdale indio_dev->channels = si7020_channels; 141064a7463SDavid Barksdale indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 142064a7463SDavid Barksdale 143064a7463SDavid Barksdale return devm_iio_device_register(&client->dev, indio_dev); 144064a7463SDavid Barksdale } 145064a7463SDavid Barksdale 146064a7463SDavid Barksdale static const struct i2c_device_id si7020_id[] = { 147064a7463SDavid Barksdale { "si7020", 0 }, 148064a7463SDavid Barksdale { } 149064a7463SDavid Barksdale }; 150064a7463SDavid Barksdale MODULE_DEVICE_TABLE(i2c, si7020_id); 151064a7463SDavid Barksdale 152064a7463SDavid Barksdale static struct i2c_driver si7020_driver = { 153064a7463SDavid Barksdale .driver.name = "si7020", 154064a7463SDavid Barksdale .probe = si7020_probe, 155064a7463SDavid Barksdale .id_table = si7020_id, 156064a7463SDavid Barksdale }; 157064a7463SDavid Barksdale 158064a7463SDavid Barksdale module_i2c_driver(si7020_driver); 159064a7463SDavid Barksdale MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 160064a7463SDavid Barksdale MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 161064a7463SDavid Barksdale MODULE_LICENSE("GPL"); 162