1*064a7463SDavid Barksdale /* 2*064a7463SDavid Barksdale * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors 3*064a7463SDavid Barksdale * Copyright (c) 2013,2014 Uplogix, Inc. 4*064a7463SDavid Barksdale * David Barksdale <dbarksdale@uplogix.com> 5*064a7463SDavid Barksdale * 6*064a7463SDavid Barksdale * This program is free software; you can redistribute it and/or modify it 7*064a7463SDavid Barksdale * under the terms and conditions of the GNU General Public License, 8*064a7463SDavid Barksdale * version 2, as published by the Free Software Foundation. 9*064a7463SDavid Barksdale * 10*064a7463SDavid Barksdale * This program is distributed in the hope that it will be useful, 11*064a7463SDavid Barksdale * but WITHOUT ANY WARRANTY; without even the implied warranty of 12*064a7463SDavid Barksdale * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*064a7463SDavid Barksdale * GNU General Public License for more details. 14*064a7463SDavid Barksdale */ 15*064a7463SDavid Barksdale 16*064a7463SDavid Barksdale /* 17*064a7463SDavid Barksdale * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors 18*064a7463SDavid Barksdale * are i2c devices which have an identical programming interface for 19*064a7463SDavid Barksdale * measuring relative humidity and temperature. The Si7013 has an additional 20*064a7463SDavid Barksdale * temperature input which this driver does not support. 21*064a7463SDavid Barksdale * 22*064a7463SDavid Barksdale * Data Sheets: 23*064a7463SDavid Barksdale * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf 24*064a7463SDavid Barksdale * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf 25*064a7463SDavid Barksdale * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf 26*064a7463SDavid Barksdale */ 27*064a7463SDavid Barksdale 28*064a7463SDavid Barksdale #include <linux/delay.h> 29*064a7463SDavid Barksdale #include <linux/i2c.h> 30*064a7463SDavid Barksdale #include <linux/module.h> 31*064a7463SDavid Barksdale #include <linux/slab.h> 32*064a7463SDavid Barksdale #include <linux/sysfs.h> 33*064a7463SDavid Barksdale 34*064a7463SDavid Barksdale #include <linux/iio/iio.h> 35*064a7463SDavid Barksdale #include <linux/iio/sysfs.h> 36*064a7463SDavid Barksdale 37*064a7463SDavid Barksdale /* Measure Relative Humidity, Hold Master Mode */ 38*064a7463SDavid Barksdale #define SI7020CMD_RH_HOLD 0xE5 39*064a7463SDavid Barksdale /* Measure Temperature, Hold Master Mode */ 40*064a7463SDavid Barksdale #define SI7020CMD_TEMP_HOLD 0xE3 41*064a7463SDavid Barksdale /* Software Reset */ 42*064a7463SDavid Barksdale #define SI7020CMD_RESET 0xFE 43*064a7463SDavid Barksdale 44*064a7463SDavid Barksdale static int si7020_read_raw(struct iio_dev *indio_dev, 45*064a7463SDavid Barksdale struct iio_chan_spec const *chan, int *val, 46*064a7463SDavid Barksdale int *val2, long mask) 47*064a7463SDavid Barksdale { 48*064a7463SDavid Barksdale struct i2c_client **client = iio_priv(indio_dev); 49*064a7463SDavid Barksdale int ret; 50*064a7463SDavid Barksdale 51*064a7463SDavid Barksdale switch (mask) { 52*064a7463SDavid Barksdale case IIO_CHAN_INFO_RAW: 53*064a7463SDavid Barksdale ret = i2c_smbus_read_word_data(*client, 54*064a7463SDavid Barksdale chan->type == IIO_TEMP ? 55*064a7463SDavid Barksdale SI7020CMD_TEMP_HOLD : 56*064a7463SDavid Barksdale SI7020CMD_RH_HOLD); 57*064a7463SDavid Barksdale if (ret < 0) 58*064a7463SDavid Barksdale return ret; 59*064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 60*064a7463SDavid Barksdale *val = ret >> 2; 61*064a7463SDavid Barksdale else 62*064a7463SDavid Barksdale *val = (ret & 0x3FFF) >> 2; 63*064a7463SDavid Barksdale return IIO_VAL_INT; 64*064a7463SDavid Barksdale case IIO_CHAN_INFO_SCALE: 65*064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 66*064a7463SDavid Barksdale *val = 175720; /* = 175.72 * 1000 */ 67*064a7463SDavid Barksdale else 68*064a7463SDavid Barksdale *val = 125 * 1000; 69*064a7463SDavid Barksdale *val2 = 65536 >> 2; 70*064a7463SDavid Barksdale return IIO_VAL_FRACTIONAL; 71*064a7463SDavid Barksdale case IIO_CHAN_INFO_OFFSET: 72*064a7463SDavid Barksdale /* 73*064a7463SDavid Barksdale * Since iio_convert_raw_to_processed_unlocked assumes offset 74*064a7463SDavid Barksdale * is an integer we have to round these values and lose 75*064a7463SDavid Barksdale * accuracy. 76*064a7463SDavid Barksdale * Relative humidity will be 0.0032959% too high and 77*064a7463SDavid Barksdale * temperature will be 0.00277344 degrees too high. 78*064a7463SDavid Barksdale * This is no big deal because it's within the accuracy of the 79*064a7463SDavid Barksdale * sensor. 80*064a7463SDavid Barksdale */ 81*064a7463SDavid Barksdale if (chan->type == IIO_TEMP) 82*064a7463SDavid Barksdale *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ 83*064a7463SDavid Barksdale else 84*064a7463SDavid Barksdale *val = -786; /* = -6 * (65536 >> 2) / 125 */ 85*064a7463SDavid Barksdale return IIO_VAL_INT; 86*064a7463SDavid Barksdale default: 87*064a7463SDavid Barksdale break; 88*064a7463SDavid Barksdale } 89*064a7463SDavid Barksdale 90*064a7463SDavid Barksdale return -EINVAL; 91*064a7463SDavid Barksdale } 92*064a7463SDavid Barksdale 93*064a7463SDavid Barksdale static const struct iio_chan_spec si7020_channels[] = { 94*064a7463SDavid Barksdale { 95*064a7463SDavid Barksdale .type = IIO_HUMIDITYRELATIVE, 96*064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 97*064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 98*064a7463SDavid Barksdale }, 99*064a7463SDavid Barksdale { 100*064a7463SDavid Barksdale .type = IIO_TEMP, 101*064a7463SDavid Barksdale .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 102*064a7463SDavid Barksdale BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), 103*064a7463SDavid Barksdale } 104*064a7463SDavid Barksdale }; 105*064a7463SDavid Barksdale 106*064a7463SDavid Barksdale static const struct iio_info si7020_info = { 107*064a7463SDavid Barksdale .read_raw = si7020_read_raw, 108*064a7463SDavid Barksdale .driver_module = THIS_MODULE, 109*064a7463SDavid Barksdale }; 110*064a7463SDavid Barksdale 111*064a7463SDavid Barksdale static int si7020_probe(struct i2c_client *client, 112*064a7463SDavid Barksdale const struct i2c_device_id *id) 113*064a7463SDavid Barksdale { 114*064a7463SDavid Barksdale struct iio_dev *indio_dev; 115*064a7463SDavid Barksdale struct i2c_client **data; 116*064a7463SDavid Barksdale int ret; 117*064a7463SDavid Barksdale 118*064a7463SDavid Barksdale if (!i2c_check_functionality(client->adapter, 119*064a7463SDavid Barksdale I2C_FUNC_SMBUS_WRITE_BYTE | 120*064a7463SDavid Barksdale I2C_FUNC_SMBUS_READ_WORD_DATA)) 121*064a7463SDavid Barksdale return -ENODEV; 122*064a7463SDavid Barksdale 123*064a7463SDavid Barksdale /* Reset device, loads default settings. */ 124*064a7463SDavid Barksdale ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); 125*064a7463SDavid Barksdale if (ret < 0) 126*064a7463SDavid Barksdale return ret; 127*064a7463SDavid Barksdale /* Wait the maximum power-up time after software reset. */ 128*064a7463SDavid Barksdale msleep(15); 129*064a7463SDavid Barksdale 130*064a7463SDavid Barksdale indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client)); 131*064a7463SDavid Barksdale if (!indio_dev) 132*064a7463SDavid Barksdale return -ENOMEM; 133*064a7463SDavid Barksdale 134*064a7463SDavid Barksdale data = iio_priv(indio_dev); 135*064a7463SDavid Barksdale *data = client; 136*064a7463SDavid Barksdale i2c_set_clientdata(client, indio_dev); 137*064a7463SDavid Barksdale 138*064a7463SDavid Barksdale indio_dev->dev.parent = &client->dev; 139*064a7463SDavid Barksdale indio_dev->name = dev_name(&client->dev); 140*064a7463SDavid Barksdale indio_dev->modes = INDIO_DIRECT_MODE; 141*064a7463SDavid Barksdale indio_dev->info = &si7020_info; 142*064a7463SDavid Barksdale indio_dev->channels = si7020_channels; 143*064a7463SDavid Barksdale indio_dev->num_channels = ARRAY_SIZE(si7020_channels); 144*064a7463SDavid Barksdale 145*064a7463SDavid Barksdale return devm_iio_device_register(&client->dev, indio_dev); 146*064a7463SDavid Barksdale } 147*064a7463SDavid Barksdale 148*064a7463SDavid Barksdale static const struct i2c_device_id si7020_id[] = { 149*064a7463SDavid Barksdale { "si7020", 0 }, 150*064a7463SDavid Barksdale { } 151*064a7463SDavid Barksdale }; 152*064a7463SDavid Barksdale MODULE_DEVICE_TABLE(i2c, si7020_id); 153*064a7463SDavid Barksdale 154*064a7463SDavid Barksdale static struct i2c_driver si7020_driver = { 155*064a7463SDavid Barksdale .driver.name = "si7020", 156*064a7463SDavid Barksdale .probe = si7020_probe, 157*064a7463SDavid Barksdale .id_table = si7020_id, 158*064a7463SDavid Barksdale }; 159*064a7463SDavid Barksdale 160*064a7463SDavid Barksdale module_i2c_driver(si7020_driver); 161*064a7463SDavid Barksdale MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); 162*064a7463SDavid Barksdale MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); 163*064a7463SDavid Barksdale MODULE_LICENSE("GPL"); 164