162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors 462306a36Sopenharmony_ci * Copyright (c) 2013,2014 Uplogix, Inc. 562306a36Sopenharmony_ci * David Barksdale <dbarksdale@uplogix.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors 1062306a36Sopenharmony_ci * are i2c devices which have an identical programming interface for 1162306a36Sopenharmony_ci * measuring relative humidity and temperature. The Si7013 has an additional 1262306a36Sopenharmony_ci * temperature input which this driver does not support. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Data Sheets: 1562306a36Sopenharmony_ci * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf 1662306a36Sopenharmony_ci * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf 1762306a36Sopenharmony_ci * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/i2c.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/sysfs.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/iio/iio.h> 2862306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Measure Relative Humidity, Hold Master Mode */ 3162306a36Sopenharmony_ci#define SI7020CMD_RH_HOLD 0xE5 3262306a36Sopenharmony_ci/* Measure Temperature, Hold Master Mode */ 3362306a36Sopenharmony_ci#define SI7020CMD_TEMP_HOLD 0xE3 3462306a36Sopenharmony_ci/* Software Reset */ 3562306a36Sopenharmony_ci#define SI7020CMD_RESET 0xFE 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int si7020_read_raw(struct iio_dev *indio_dev, 3862306a36Sopenharmony_ci struct iio_chan_spec const *chan, int *val, 3962306a36Sopenharmony_ci int *val2, long mask) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct i2c_client **client = iio_priv(indio_dev); 4262306a36Sopenharmony_ci int ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci switch (mask) { 4562306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 4662306a36Sopenharmony_ci ret = i2c_smbus_read_word_swapped(*client, 4762306a36Sopenharmony_ci chan->type == IIO_TEMP ? 4862306a36Sopenharmony_ci SI7020CMD_TEMP_HOLD : 4962306a36Sopenharmony_ci SI7020CMD_RH_HOLD); 5062306a36Sopenharmony_ci if (ret < 0) 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci *val = ret >> 2; 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * Humidity values can slightly exceed the 0-100%RH 5562306a36Sopenharmony_ci * range and should be corrected by software 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci if (chan->type == IIO_HUMIDITYRELATIVE) 5862306a36Sopenharmony_ci *val = clamp_val(*val, 786, 13893); 5962306a36Sopenharmony_ci return IIO_VAL_INT; 6062306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 6162306a36Sopenharmony_ci if (chan->type == IIO_TEMP) 6262306a36Sopenharmony_ci *val = 175720; /* = 175.72 * 1000 */ 6362306a36Sopenharmony_ci else 6462306a36Sopenharmony_ci *val = 125 * 1000; 6562306a36Sopenharmony_ci *val2 = 65536 >> 2; 6662306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL; 6762306a36Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Since iio_convert_raw_to_processed_unlocked assumes offset 7062306a36Sopenharmony_ci * is an integer we have to round these values and lose 7162306a36Sopenharmony_ci * accuracy. 7262306a36Sopenharmony_ci * Relative humidity will be 0.0032959% too high and 7362306a36Sopenharmony_ci * temperature will be 0.00277344 degrees too high. 7462306a36Sopenharmony_ci * This is no big deal because it's within the accuracy of the 7562306a36Sopenharmony_ci * sensor. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci if (chan->type == IIO_TEMP) 7862306a36Sopenharmony_ci *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci *val = -786; /* = -6 * (65536 >> 2) / 125 */ 8162306a36Sopenharmony_ci return IIO_VAL_INT; 8262306a36Sopenharmony_ci default: 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic const struct iio_chan_spec si7020_channels[] = { 9062306a36Sopenharmony_ci { 9162306a36Sopenharmony_ci .type = IIO_HUMIDITYRELATIVE, 9262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 9362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 9462306a36Sopenharmony_ci }, 9562306a36Sopenharmony_ci { 9662306a36Sopenharmony_ci .type = IIO_TEMP, 9762306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 9862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const struct iio_info si7020_info = { 10362306a36Sopenharmony_ci .read_raw = si7020_read_raw, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int si7020_probe(struct i2c_client *client) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct iio_dev *indio_dev; 10962306a36Sopenharmony_ci struct i2c_client **data; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 11362306a36Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_BYTE | 11462306a36Sopenharmony_ci I2C_FUNC_SMBUS_READ_WORD_DATA)) 11562306a36Sopenharmony_ci return -EOPNOTSUPP; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Reset device, loads default settings. */ 11862306a36Sopenharmony_ci ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); 11962306a36Sopenharmony_ci if (ret < 0) 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci /* Wait the maximum power-up time after software reset. */ 12262306a36Sopenharmony_ci msleep(15); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 12562306a36Sopenharmony_ci if (!indio_dev) 12662306a36Sopenharmony_ci return -ENOMEM; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci data = iio_priv(indio_dev); 12962306a36Sopenharmony_ci *data = client; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci indio_dev->name = dev_name(&client->dev); 13262306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 13362306a36Sopenharmony_ci indio_dev->info = &si7020_info; 13462306a36Sopenharmony_ci indio_dev->channels = si7020_channels; 13562306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct i2c_device_id si7020_id[] = { 14162306a36Sopenharmony_ci { "si7020", 0 }, 14262306a36Sopenharmony_ci { "th06", 0 }, 14362306a36Sopenharmony_ci { } 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si7020_id); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const struct of_device_id si7020_dt_ids[] = { 14862306a36Sopenharmony_ci { .compatible = "silabs,si7020" }, 14962306a36Sopenharmony_ci { } 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, si7020_dt_ids); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct i2c_driver si7020_driver = { 15462306a36Sopenharmony_ci .driver = { 15562306a36Sopenharmony_ci .name = "si7020", 15662306a36Sopenharmony_ci .of_match_table = si7020_dt_ids, 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci .probe = si7020_probe, 15962306a36Sopenharmony_ci .id_table = si7020_id, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cimodule_i2c_driver(si7020_driver); 16362306a36Sopenharmony_ciMODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 16462306a36Sopenharmony_ciMODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 16562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 166