1*4c3dd9cdSThomas 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> 23064a7463SDavid Barksdale #include <linux/slab.h> 24064a7463SDavid Barksdale #include <linux/sysfs.h> 25064a7463SDavid Barksdale 26064a7463SDavid Barksdale #include <linux/iio/iio.h> 27064a7463SDavid Barksdale #include <linux/iio/sysfs.h> 28064a7463SDavid Barksdale 29064a7463SDavid Barksdale /* Measure Relative Humidity, Hold Master Mode */ 30064a7463SDavid Barksdale #define SI7020CMD_RH_HOLD 0xE5 31064a7463SDavid Barksdale /* Measure Temperature, Hold Master Mode */ 32064a7463SDavid Barksdale #define SI7020CMD_TEMP_HOLD 0xE3 33064a7463SDavid Barksdale /* Software Reset */ 34064a7463SDavid Barksdale #define SI7020CMD_RESET 0xFE 35064a7463SDavid Barksdale 36064a7463SDavid Barksdale static int si7020_read_raw(struct iio_dev *indio_dev, 37064a7463SDavid Barksdale struct iio_chan_spec const *chan, int *val, 38064a7463SDavid Barksdale int *val2, long mask) 39064a7463SDavid Barksdale { 40e765537aSJonathan Cameron struct i2c_client **client = iio_priv(indio_dev); 41064a7463SDavid Barksdale int ret; 42064a7463SDavid Barksdale 43064a7463SDavid Barksdale switch (mask) { 44064a7463SDavid Barksdale case IIO_CHAN_INFO_RAW: 450d2f6fd3SChris Lesiak ret = i2c_smbus_read_word_swapped(*client, 46064a7463SDavid Barksdale chan->type == IIO_TEMP ? 47064a7463SDavid Barksdale SI7020CMD_TEMP_HOLD : 48064a7463SDavid Barksdale SI7020CMD_RH_HOLD); 49064a7463SDavid Barksdale if (ret < 0) 50064a7463SDavid Barksdale return ret; 51064a7463SDavid Barksdale *val = ret >> 2; 52ecf7e207SNicola Corna /* 53ecf7e207SNicola Corna * Humidity values can slightly exceed the 0-100%RH 54ecf7e207SNicola Corna * range and should be corrected by software 55ecf7e207SNicola Corna */ 56345b4830SHartmut Knaack if (chan->type == IIO_HUMIDITYRELATIVE) 57ecf7e207SNicola Corna *val = clamp_val(*val, 786, 13893); 58064a7463SDavid Barksdale return IIO_VAL_INT; 59064a7463SDavid Barksdale case IIO_CHAN_INFO_SCALE: 60064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 61064a7463SDavid Barksdale *val = 175720; /* = 175.72 * 1000 */ 62064a7463SDavid Barksdale else 63064a7463SDavid Barksdale *val = 125 * 1000; 64064a7463SDavid Barksdale *val2 = 65536 >> 2; 65064a7463SDavid Barksdale return IIO_VAL_FRACTIONAL; 66064a7463SDavid Barksdale case IIO_CHAN_INFO_OFFSET: 67064a7463SDavid Barksdale /* 68064a7463SDavid Barksdale * Since iio_convert_raw_to_processed_unlocked assumes offset 69064a7463SDavid Barksdale * is an integer we have to round these values and lose 70064a7463SDavid Barksdale * accuracy. 71064a7463SDavid Barksdale * Relative humidity will be 0.0032959% too high and 72064a7463SDavid Barksdale * temperature will be 0.00277344 degrees too high. 73064a7463SDavid Barksdale * This is no big deal because it's within the accuracy of the 74064a7463SDavid Barksdale * sensor. 75064a7463SDavid Barksdale */ 76064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 77064a7463SDavid Barksdale *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 78064a7463SDavid Barksdale else 79064a7463SDavid Barksdale *val = -786; /* = -6 * (65536 >> 2) / 125 */ 80064a7463SDavid Barksdale return IIO_VAL_INT; 81064a7463SDavid Barksdale default: 82064a7463SDavid Barksdale break; 83064a7463SDavid Barksdale } 84064a7463SDavid Barksdale 85064a7463SDavid Barksdale return -EINVAL; 86064a7463SDavid Barksdale } 87064a7463SDavid Barksdale 88064a7463SDavid Barksdale static const struct iio_chan_spec si7020_channels[] = { 89064a7463SDavid Barksdale { 90064a7463SDavid Barksdale .type = IIO_HUMIDITYRELATIVE, 91064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 92064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 93064a7463SDavid Barksdale }, 94064a7463SDavid Barksdale { 95064a7463SDavid Barksdale .type = IIO_TEMP, 96064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 97064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 98064a7463SDavid Barksdale } 99064a7463SDavid Barksdale }; 100064a7463SDavid Barksdale 101064a7463SDavid Barksdale static const struct iio_info si7020_info = { 102064a7463SDavid Barksdale .read_raw = si7020_read_raw, 103064a7463SDavid Barksdale }; 104064a7463SDavid Barksdale 105064a7463SDavid Barksdale static int si7020_probe(struct i2c_client *client, 106064a7463SDavid Barksdale const struct i2c_device_id *id) 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->dev.parent = &client->dev; 132064a7463SDavid Barksdale indio_dev->name = dev_name(&client->dev); 133064a7463SDavid Barksdale indio_dev->modes = INDIO_DIRECT_MODE; 134064a7463SDavid Barksdale indio_dev->info = &si7020_info; 135064a7463SDavid Barksdale indio_dev->channels = si7020_channels; 136064a7463SDavid Barksdale indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 137064a7463SDavid Barksdale 138064a7463SDavid Barksdale return devm_iio_device_register(&client->dev, indio_dev); 139064a7463SDavid Barksdale } 140064a7463SDavid Barksdale 141064a7463SDavid Barksdale static const struct i2c_device_id si7020_id[] = { 142064a7463SDavid Barksdale { "si7020", 0 }, 143920dad0cSCristina Moraru { "th06", 0 }, 144064a7463SDavid Barksdale { } 145064a7463SDavid Barksdale }; 146064a7463SDavid Barksdale MODULE_DEVICE_TABLE(i2c, si7020_id); 147064a7463SDavid Barksdale 148b0d80198SPaul Kocialkowski static const struct of_device_id si7020_dt_ids[] = { 149b0d80198SPaul Kocialkowski { .compatible = "silabs,si7020" }, 150b0d80198SPaul Kocialkowski { } 151b0d80198SPaul Kocialkowski }; 152b0d80198SPaul Kocialkowski MODULE_DEVICE_TABLE(of, si7020_dt_ids); 153b0d80198SPaul Kocialkowski 154064a7463SDavid Barksdale static struct i2c_driver si7020_driver = { 155b0d80198SPaul Kocialkowski .driver = { 156b0d80198SPaul Kocialkowski .name = "si7020", 157b0d80198SPaul Kocialkowski .of_match_table = of_match_ptr(si7020_dt_ids), 158b0d80198SPaul Kocialkowski }, 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